Merge "Accept APK install with v4 signature to set up fs-verity" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0aa9ea8..3abaa69 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -12,30 +12,38 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+aconfig_srcjars = [
+    ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+    ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+    ":android.os.flags-aconfig-java{.generated_srcjars}",
+    ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
+    ":android.security.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.flags-aconfig-java{.generated_srcjars}",
+    ":camera_platform_flags_core_java_lib{.generated_srcjars}",
+    ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
+    ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
+    ":com.android.text.flags-aconfig-java{.generated_srcjars}",
+    ":telecom_flags_core_java_lib{.generated_srcjars}",
+    ":telephony_flags_core_java_lib{.generated_srcjars}",
+    ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
+    ":android.widget.flags-aconfig-java{.generated_srcjars}",
+    ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
+    ":sdk_sandbox_flags_lib{.generated_srcjars}",
+    ":android.permission.flags-aconfig-java{.generated_srcjars}",
+    ":hwui_flags_java_lib{.generated_srcjars}",
+]
+
+filegroup {
+    name: "framework-minus-apex-aconfig-srcjars",
+    srcs: aconfig_srcjars,
+}
+
 // Aconfig declarations and libraries for the core framework
 java_defaults {
     name: "framework-minus-apex-aconfig-libraries",
-
     // Add java_aconfig_libraries to here to add them to the core framework
-    srcs: [
-        ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
-        ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
-        ":android.os.flags-aconfig-java{.generated_srcjars}",
-        ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
-        ":android.security.flags-aconfig-java{.generated_srcjars}",
-        ":android.view.flags-aconfig-java{.generated_srcjars}",
-        ":camera_platform_flags_core_java_lib{.generated_srcjars}",
-        ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
-        ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
-        ":com.android.text.flags-aconfig-java{.generated_srcjars}",
-        ":telecom_flags_core_java_lib{.generated_srcjars}",
-        ":telephony_flags_core_java_lib{.generated_srcjars}",
-        ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
-        ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
-        ":android.widget.flags-aconfig-java{.generated_srcjars}",
-        ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
-        ":sdk_sandbox_flags_lib{.generated_srcjars}",
-    ],
+    srcs: aconfig_srcjars,
     // Add aconfig-annotations-lib as a dependency for the optimization
     libs: ["aconfig-annotations-lib"],
 }
@@ -249,3 +257,23 @@
     aconfig_declarations: "com.android.media.flags.bettertogether-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Permissions
+aconfig_declarations {
+    name: "android.permission.flags-aconfig",
+    package: "android.permission.flags",
+    srcs: ["core/java/android/permission/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.permission.flags-aconfig-java",
+    aconfig_declarations: "android.permission.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Graphics
+java_aconfig_library {
+    name: "hwui_flags_java_lib",
+    aconfig_declarations: "hwui_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 57a5a3c..f1a3af2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -621,6 +621,7 @@
     "--api-lint-ignore-prefix org. " +
     "--error NoSettingsProvider " +
     "--error UnhiddenSystemApi " +
+    "--error UnflaggedApi " +
     "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
     "--hide BroadcastBehavior " +
     "--hide CallbackInterface " +
diff --git a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
index 9699757..75838c2 100644
--- a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
+++ b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java
@@ -49,13 +49,25 @@
     String QUICK_DOZE_REQUEST_IDENTIFIER = "quick_doze_request";
 
     /**
+     * Mode manager off body state identifier.
+     *
+     * <p>Unique identifier that can be used as identifier parameter in
+     * registerInternalStateObserver
+     * to listen to changes in quick doze request state from mode manager.
+     *
+     * TODO(b/288276510): convert to int constant
+     */
+    String OFFBODY_STATE_ID = "off_body";
+
+    /**
      * StringDef for Mode manager identifiers.
      *
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
     @StringDef({
-            QUICK_DOZE_REQUEST_IDENTIFIER
+            QUICK_DOZE_REQUEST_IDENTIFIER,
+            OFFBODY_STATE_ID
     })
     @Target(ElementType.TYPE_USE)
     @interface Identifier {
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index b8596d5..f252a0b 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -377,7 +377,11 @@
     @GuardedBy("this")
     private boolean mModeManagerRequestedQuickDoze;
     @GuardedBy("this")
+    private boolean mIsOffBody;
+    @GuardedBy("this")
     private boolean mForceModeManagerQuickDozeRequest;
+    @GuardedBy("this")
+    private boolean mForceModeManagerOffBodyState;
 
     /** Time in the elapsed realtime timebase when this listener last received a motion event. */
     @GuardedBy("this")
@@ -437,6 +441,7 @@
     private static final int ACTIVE_REASON_ALARM = 7;
     private static final int ACTIVE_REASON_EMERGENCY_CALL = 8;
     private static final int ACTIVE_REASON_MODE_MANAGER = 9;
+    private static final int ACTIVE_REASON_ONBODY = 10;
 
     @VisibleForTesting
     static String stateToString(int state) {
@@ -837,7 +842,7 @@
     class ModeManagerQuickDozeRequestConsumer implements Consumer<Boolean> {
         @Override
         public void accept(Boolean enabled) {
-            Slog.d(TAG, "Mode manager quick doze request: " + enabled);
+            Slog.i(TAG, "Mode manager quick doze request: " + enabled);
             synchronized (DeviceIdleController.this) {
                 if (!mForceModeManagerQuickDozeRequest
                         && mModeManagerRequestedQuickDoze != enabled) {
@@ -848,13 +853,46 @@
         }
 
         @GuardedBy("DeviceIdleController.this")
-        public void onModeManagerRequestChangedLocked() {
+        private void onModeManagerRequestChangedLocked() {
             // Get into quick doze faster when mode manager requests instead of taking
             // traditional multi-stage approach.
+            maybeBecomeActiveOnModeManagerEventsLocked();
             updateQuickDozeFlagLocked();
-            if (!mModeManagerRequestedQuickDoze && !mBatterySaverEnabled) {
-                mActiveReason = ACTIVE_REASON_MODE_MANAGER;
-                becomeActiveLocked("mode_manager", Process.myUid());
+        }
+    }
+
+    @VisibleForTesting
+    class ModeManagerOffBodyStateConsumer implements Consumer<Boolean> {
+        @Override
+        public void accept(Boolean isOffBody) {
+            Slog.i(TAG, "Offbody event from mode manager: " + isOffBody);
+            synchronized (DeviceIdleController.this) {
+                if (!mForceModeManagerOffBodyState && mIsOffBody != isOffBody) {
+                    mIsOffBody = isOffBody;
+                    onModeManagerOffBodyChangedLocked();
+                }
+            }
+        }
+
+        @GuardedBy("DeviceIdleController.this")
+        private void onModeManagerOffBodyChangedLocked() {
+            maybeBecomeActiveOnModeManagerEventsLocked();
+        }
+    }
+
+    @GuardedBy("DeviceIdleController.this")
+    private void maybeBecomeActiveOnModeManagerEventsLocked() {
+        synchronized (DeviceIdleController.this) {
+            if (mQuickDozeActivated) {
+                // Quick doze is enabled so don't turn the device active.
+                return;
+            }
+            // Fall through when quick doze is not requested.
+
+            if (!mIsOffBody) {
+                // Quick doze was not requested and device is on body so turn the device active.
+                mActiveReason = ACTIVE_REASON_ONBODY;
+                becomeActiveLocked("on_body", Process.myUid());
             }
         }
     }
@@ -864,6 +902,10 @@
             new ModeManagerQuickDozeRequestConsumer();
 
     @VisibleForTesting
+    final ModeManagerOffBodyStateConsumer mModeManagerOffBodyStateConsumer =
+            new ModeManagerOffBodyStateConsumer();
+
+    @VisibleForTesting
     final class MotionListener extends TriggerEventListener
             implements SensorEventListener {
 
@@ -2648,6 +2690,12 @@
                                 WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER,
                                 AppSchedulingModuleThread.getExecutor(),
                                 mModeManagerQuickDozeRequestConsumer);
+
+                        modeManagerInternal.addActiveStateChangeListener(
+                                WearModeManagerInternal.OFFBODY_STATE_ID,
+                                AppSchedulingModuleThread.getExecutor(),
+                                mModeManagerOffBodyStateConsumer
+                        );
                     }
                 }
                 mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,
@@ -4463,7 +4511,7 @@
         pw.println(
                 "    Resume normal functioning after force-idle or force-inactive or "
                         + "force-modemanager-quickdoze.");
-        pw.println("  get [light|deep|force|screen|charging|network|offbody|forcebodystate]");
+        pw.println("  get [light|deep|force|screen|charging|network|offbody|forceoffbody]");
         pw.println("    Retrieve the current given state.");
         pw.println("  disable [light|deep|all]");
         pw.println("    Completely disable device idle mode.");
@@ -4500,6 +4548,10 @@
         pw.println("  force-modemanager-quickdoze [true|false]");
         pw.println("    Simulate mode manager request to enable (true) or disable (false) "
                 + "quick doze. Mode manager changes will be ignored until unforce is called.");
+        pw.println("  force-modemanager-offbody [true|false]");
+        pw.println("    Force mode manager offbody state, this can be used to simulate "
+                + "device being off-body (true) or on-body (false). Mode manager changes "
+                + "will be ignored until unforce is called.");
     }
 
     class Shell extends ShellCommand {
@@ -4634,6 +4686,9 @@
                     mForceModeManagerQuickDozeRequest = false;
                     pw.println("mForceModeManagerQuickDozeRequest: "
                             + mForceModeManagerQuickDozeRequest);
+                    mForceModeManagerOffBodyState = false;
+                    pw.println("mForceModeManagerOffBodyState: "
+                            + mForceModeManagerOffBodyState);
                 } finally {
                     Binder.restoreCallingIdentity(token);
                 }
@@ -4660,6 +4715,8 @@
                             case "forcemodemanagerquick":
                                 pw.println(mForceModeManagerQuickDozeRequest);
                                 break;
+                            case "offbody": pw.println(mIsOffBody); break;
+                            case "forceoffbody": pw.println(mForceModeManagerOffBodyState); break;
                             default: pw.println("Unknown get option: " + arg); break;
                         }
                     } finally {
@@ -4982,6 +5039,31 @@
                 pw.println("Provide true or false argument after force-modemanager-quickdoze");
                 return -1;
             }
+        } else if ("force-modemanager-offbody".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            String arg = shell.getNextArg();
+
+            if ("true".equalsIgnoreCase(arg) || "false".equalsIgnoreCase(arg)) {
+                boolean isOffBody = Boolean.parseBoolean(arg);
+
+                synchronized (DeviceIdleController.this) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mForceModeManagerOffBodyState = true;
+                        pw.println("mForceModeManagerOffBodyState: "
+                                + mForceModeManagerOffBodyState);
+                        mIsOffBody = isOffBody;
+                        pw.println("mIsOffBody: " + mIsOffBody);
+                        mModeManagerOffBodyStateConsumer.onModeManagerOffBodyChangedLocked();
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            } else {
+                pw.println("Provide true or false argument after force-modemanager-offbody");
+                return -1;
+            }
         } else {
             return shell.handleDefaultCommands(cmd);
         }
@@ -5233,6 +5315,12 @@
             if (mAlarmsActive) {
                 pw.print("  mAlarmsActive="); pw.println(mAlarmsActive);
             }
+            if (mConstants.USE_MODE_MANAGER) {
+                pw.print("  mModeManagerRequestedQuickDoze=");
+                pw.println(mModeManagerRequestedQuickDoze);
+                pw.print("  mIsOffBody=");
+                pw.println(mIsOffBody);
+            }
         }
     }
 
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 2f84df7..8989b10 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -29,11 +29,14 @@
 
 droidstubs {
     name: "api-stubs-docs-non-updatable",
+    srcs: [
+        ":framework-minus-apex-aconfig-srcjars",
+    ],
     defaults: [
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
     ],
-    args: metalava_framework_docs_args + "--error UnflaggedApi ",
+    args: metalava_framework_docs_args,
     check_api: {
         current: {
             api_file: ":non-updatable-current.txt",
@@ -74,8 +77,7 @@
     "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
     "\\)"
 
-test = " --show-annotation android.annotation.TestApi" +
-    " --hide UnflaggedApi" // TODO(b/297362755): TestApi lint doesn't ignore existing APIs.
+test = " --show-annotation android.annotation.TestApi"
 
 module_libs = " --show-annotation android.annotation.SystemApi\\(" +
     "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
@@ -134,6 +136,7 @@
         },
         api_lint: {
             enabled: true,
+            new_since: ":android.api.test.latest",
             baseline_file: ":non-updatable-test-lint-baseline.txt",
         },
     },
diff --git a/api/api.go b/api/api.go
index 6095a9a..83804c6 100644
--- a/api/api.go
+++ b/api/api.go
@@ -433,7 +433,7 @@
 }
 
 // combined_apis bp2build converter
-func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *CombinedApis) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	basePrefix := "non-updatable"
 	scopeToSuffix := map[string]string{
 		"public":        "-current.txt",
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index 9225fe8..71f366a 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@
 
 # Convert each removed.txt to the "dex format" equivalent, and print all output.
 for f in "$@"; do
-    "$metalava_path" --no-banner "$f" --dex-api "${tmp_dir}/tmp"
+    "$metalava_path" "$f" --dex-api "${tmp_dir}/tmp"
     cat "${tmp_dir}/tmp"
 done
diff --git a/core/api/current.txt b/core/api/current.txt
index f488c82..904b0d2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9681,22 +9681,22 @@
   public final class VirtualDevice implements android.os.Parcelable {
     method public int describeContents();
     method public int getDeviceId();
-    method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) @NonNull public int[] getDisplayIds();
+    method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @NonNull public int[] getDisplayIds();
     method @Nullable public String getName();
     method @Nullable public String getPersistentDeviceId();
-    method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public boolean hasCustomSensorSupport();
+    method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomSensorSupport();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR;
   }
 
   public final class VirtualDeviceManager {
-    method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) @Nullable public android.companion.virtual.VirtualDevice getVirtualDevice(int);
+    method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public android.companion.virtual.VirtualDevice getVirtualDevice(int);
     method @NonNull public java.util.List<android.companion.virtual.VirtualDevice> getVirtualDevices();
-    method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public void registerVirtualDeviceListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
-    method @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public void unregisterVirtualDeviceListener(@NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
+    method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public void registerVirtualDeviceListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
+    method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public void unregisterVirtualDeviceListener(@NonNull android.companion.virtual.VirtualDeviceManager.VirtualDeviceListener);
   }
 
-  @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public static interface VirtualDeviceManager.VirtualDeviceListener {
+  @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public static interface VirtualDeviceManager.VirtualDeviceListener {
     method public default void onVirtualDeviceClosed(int);
     method public default void onVirtualDeviceCreated(int);
   }
@@ -17653,13 +17653,13 @@
 package android.graphics.text {
 
   public final class LineBreakConfig {
-    method @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public int getHyphenation();
+    method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public int getHyphenation();
     method public int getLineBreakStyle();
     method public int getLineBreakWordStyle();
     method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig);
-    field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_DISABLED = 0; // 0x0
-    field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_ENABLED = 1; // 0x1
-    field @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
+    field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_DISABLED = 0; // 0x0
+    field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_ENABLED = 1; // 0x1
+    field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
     field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
     field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
     field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
@@ -17674,7 +17674,7 @@
     ctor public LineBreakConfig.Builder();
     method @NonNull public android.graphics.text.LineBreakConfig build();
     method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig);
-    method @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) @NonNull public android.graphics.text.LineBreakConfig.Builder setHyphenation(int);
+    method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig.Builder setHyphenation(int);
     method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int);
     method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int);
   }
@@ -23951,12 +23951,12 @@
     method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
     method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
     method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
-    method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) @Nullable public android.media.RouteListingPreference getRouteListingPreference();
+    method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") @Nullable public android.media.RouteListingPreference getRouteListingPreference();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
     method @NonNull public android.media.MediaRouter2.RoutingController getSystemController();
     method public void registerControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.ControllerCallback);
     method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference);
-    method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void registerRouteListingPreferenceCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
+    method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public void registerRouteListingPreferenceCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
     method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
     method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
     method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
@@ -23965,7 +23965,7 @@
     method public void transferTo(@NonNull android.media.MediaRoute2Info);
     method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
     method public void unregisterRouteCallback(@NonNull android.media.MediaRouter2.RouteCallback);
-    method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void unregisterRouteListingPreferenceCallback(@NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
+    method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public void unregisterRouteListingPreferenceCallback(@NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
     method public void unregisterTransferCallback(@NonNull android.media.MediaRouter2.TransferCallback);
   }
 
@@ -23986,9 +23986,9 @@
     method public void onRoutesUpdated(@NonNull java.util.List<android.media.MediaRoute2Info>);
   }
 
-  @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public abstract static class MediaRouter2.RouteListingPreferenceCallback {
-    ctor @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public MediaRouter2.RouteListingPreferenceCallback();
-    method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void onRouteListingPreferenceChanged(@Nullable android.media.RouteListingPreference);
+  @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public abstract static class MediaRouter2.RouteListingPreferenceCallback {
+    ctor @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public MediaRouter2.RouteListingPreferenceCallback();
+    method @FlaggedApi("com.android.media.flags.enable_rlp_callbacks_in_media_router2") public void onRouteListingPreferenceChanged(@Nullable android.media.RouteListingPreference);
   }
 
   public class MediaRouter2.RoutingController {
@@ -32285,6 +32285,7 @@
     method public static long getNativeHeapFreeSize();
     method public static long getNativeHeapSize();
     method public static long getPss();
+    method @FlaggedApi("android.os.remove_app_profiler_pss_collection") public static long getRss();
     method public static String getRuntimeStat(String);
     method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
     method @Deprecated public static int getThreadAllocCount();
@@ -38656,10 +38657,10 @@
   }
 
   public final class FileIntegrityManager {
-    method @FlaggedApi(Flags.FLAG_FSVERITY_API) @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
+    method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
     method public boolean isApkVeritySupported();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException;
-    method @FlaggedApi(Flags.FLAG_FSVERITY_API) public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
+    method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
   }
 
   public final class KeyChain {
@@ -41578,7 +41579,7 @@
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
-    field @FlaggedApi(Flags.FLAG_VOIP_APP_ACTIONS_SUPPORT) public static final int PROPERTY_IS_TRANSACTIONAL = 32768; // 0x8000
+    field @FlaggedApi("com.android.server.telecom.flags.voip_app_actions_support") public static final int PROPERTY_IS_TRANSACTIONAL = 32768; // 0x8000
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
@@ -44887,6 +44888,7 @@
     method public int getSubscriptionType();
     method public int getUsageSetting();
     method public boolean isEmbedded();
+    method @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public boolean isNtn();
     method public boolean isOpportunistic();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SubscriptionInfo> CREATOR;
@@ -46614,7 +46616,7 @@
 
   public static class BoringLayout.Metrics extends android.graphics.Paint.FontMetricsInt {
     ctor public BoringLayout.Metrics();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.RectF getDrawingBoundingBox();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.RectF getDrawingBoundingBox();
     field public int width;
   }
 
@@ -46799,7 +46801,7 @@
 
   public abstract class Layout {
     ctor protected Layout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.RectF computeDrawingBoundingBox();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.RectF computeDrawingBoundingBox();
     method public void draw(android.graphics.Canvas);
     method public void draw(android.graphics.Canvas, android.graphics.Path, android.graphics.Paint, int);
     method public void draw(@NonNull android.graphics.Canvas, @Nullable java.util.List<android.graphics.Path>, @Nullable java.util.List<android.graphics.Paint>, @Nullable android.graphics.Path, @Nullable android.graphics.Paint, int);
@@ -46808,24 +46810,24 @@
     method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
     method @NonNull public final android.text.Layout.Alignment getAlignment();
     method public abstract int getBottomPadding();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getBreakStrategy();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final int getBreakStrategy();
     method public void getCursorPath(int, android.graphics.Path, CharSequence);
     method public static float getDesiredWidth(CharSequence, android.text.TextPaint);
     method public static float getDesiredWidth(CharSequence, int, int, android.text.TextPaint);
     method public abstract int getEllipsisCount(int);
     method public abstract int getEllipsisStart(int);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
     method @IntRange(from=0) public int getEllipsizedWidth();
     method public int getHeight();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getHyphenationFrequency();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getJustificationMode();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final int[] getLeftIndents();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final int getHyphenationFrequency();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final int getJustificationMode();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getLeftIndents();
     method public final int getLineAscent(int);
     method public final int getLineBaseline(int);
     method public final int getLineBottom(int);
     method public int getLineBottom(int, boolean);
     method public int getLineBounds(int, android.graphics.Rect);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
     method public abstract boolean getLineContainsTab(int);
     method public abstract int getLineCount();
     method public abstract int getLineDescent(int);
@@ -46836,13 +46838,13 @@
     method public float getLineLeft(int);
     method public float getLineMax(int);
     method public float getLineRight(int);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final float getLineSpacingAmount();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final float getLineSpacingMultiplier();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingMultiplier();
     method public abstract int getLineStart(int);
     method public abstract int getLineTop(int);
     method public int getLineVisibleEnd(int);
     method public float getLineWidth(int);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @IntRange(from=1) public final int getMaxLines();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @IntRange(from=1) public final int getMaxLines();
     method public int getOffsetForHorizontal(int, float);
     method public int getOffsetToLeftOf(int);
     method public int getOffsetToRightOf(int);
@@ -46853,19 +46855,19 @@
     method public final int getParagraphRight(int);
     method public float getPrimaryHorizontal(int);
     method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final int[] getRightIndents();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getRightIndents();
     method public float getSecondaryHorizontal(int);
     method public void getSelectionPath(int, int, android.graphics.Path);
     method public final float getSpacingAdd();
     method public final float getSpacingMultiplier();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public final CharSequence getText();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public final CharSequence getText();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
     method public abstract int getTopPadding();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public boolean getUseBoundsForWidth();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getUseBoundsForWidth();
     method @IntRange(from=0) public final int getWidth();
     method public final void increaseWidthTo(int);
     method public boolean isFallbackLineSpacingEnabled();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final boolean isFontPaddingIncluded();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final boolean isFontPaddingIncluded();
     method public boolean isRtlCharAt(int);
     method protected final boolean isSpanned();
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -46893,7 +46895,7 @@
     enum_constant public static final android.text.Layout.Alignment ALIGN_OPPOSITE;
   }
 
-  @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public static final class Layout.Builder {
+  @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final class Layout.Builder {
     ctor public Layout.Builder(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextPaint, @IntRange(from=0) int);
     method @NonNull public android.text.Layout build();
     method @NonNull public android.text.Layout.Builder setAlignment(@NonNull android.text.Layout.Alignment);
@@ -46911,7 +46913,7 @@
     method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
     method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
     method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
   }
 
   public static class Layout.Directions {
@@ -47888,13 +47890,13 @@
     method public void writeToParcel(@NonNull android.os.Parcel, int);
   }
 
-  @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public class LineBreakConfigSpan {
+  @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public class LineBreakConfigSpan {
     ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig);
     method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
   }
 
-  @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
-    ctor @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public LineBreakConfigSpan.NoHyphenationSpan();
+  @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
+    ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoHyphenationSpan();
   }
 
   public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
@@ -50057,7 +50059,7 @@
     field public static final int VIRTUAL_KEY_RELEASE = 8; // 0x8
   }
 
-  @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public class HapticScrollFeedbackProvider implements android.view.ScrollFeedbackProvider {
+  @FlaggedApi("android.view.flags.scroll_feedback_api") public class HapticScrollFeedbackProvider implements android.view.ScrollFeedbackProvider {
     ctor public HapticScrollFeedbackProvider(@NonNull android.view.View);
     method public void onScrollLimit(int, int, int, boolean);
     method public void onScrollProgress(int, int, int, int);
@@ -51229,7 +51231,7 @@
     method @UiThread public void updatePositionInWindow();
   }
 
-  @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public interface ScrollFeedbackProvider {
+  @FlaggedApi("android.view.flags.scroll_feedback_api") public interface ScrollFeedbackProvider {
     method public void onScrollLimit(int, int, int, boolean);
     method public void onScrollProgress(int, int, int, int);
     method public void onSnapToItem(int, int, int);
@@ -52512,7 +52514,7 @@
     method @Deprecated public static int getEdgeSlop();
     method @Deprecated public static int getFadingEdgeLength();
     method @Deprecated public static long getGlobalActionKeyTimeout();
-    method @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public int getHapticScrollFeedbackTickInterval(int, int, int);
+    method @FlaggedApi("android.view.flags.scroll_feedback_api") public int getHapticScrollFeedbackTickInterval(int, int, int);
     method public static int getJumpTapTimeout();
     method public static int getKeyRepeatDelay();
     method public static int getKeyRepeatTimeout();
@@ -52552,7 +52554,7 @@
     method @Deprecated public static int getWindowTouchSlop();
     method public static long getZoomControlsTimeout();
     method public boolean hasPermanentMenuKey();
-    method @FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) public boolean isHapticScrollFeedbackEnabled(int, int, int);
+    method @FlaggedApi("android.view.flags.scroll_feedback_api") public boolean isHapticScrollFeedbackEnabled(int, int, int);
     method public boolean shouldShowMenuShortcutsWhenKeyboardPresent();
   }
 
@@ -59885,7 +59887,7 @@
     method public final android.text.method.TransformationMethod getTransformationMethod();
     method public android.graphics.Typeface getTypeface();
     method public android.text.style.URLSpan[] getUrls();
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public boolean getUseBoundsForWidth();
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getUseBoundsForWidth();
     method public boolean hasSelection();
     method public boolean isAllCaps();
     method public boolean isCursorVisible();
@@ -60028,7 +60030,7 @@
     method public final void setTransformationMethod(android.text.method.TransformationMethod);
     method public void setTypeface(@Nullable android.graphics.Typeface, int);
     method public void setTypeface(@Nullable android.graphics.Typeface);
-    method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public void setUseBoundsForWidth(boolean);
+    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public void setUseBoundsForWidth(boolean);
     method public void setWidth(int);
     field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
     field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 6b7910a..afb10f5 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -265,8 +265,6 @@
     New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.reset()
 UnflaggedApi: android.database.sqlite.SQLiteRawStatement#step():
     New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.step()
-UnflaggedApi: android.database.sqlite.SQLiteRawStatement#toString():
-    New API must be flagged with @FlaggedApi: method android.database.sqlite.SQLiteRawStatement.toString()
 UnflaggedApi: android.graphics.Gainmap#Gainmap(android.graphics.Gainmap, android.graphics.Bitmap):
     New API must be flagged with @FlaggedApi: constructor android.graphics.Gainmap(android.graphics.Gainmap,android.graphics.Bitmap)
 UnflaggedApi: android.graphics.Path#computeBounds(android.graphics.RectF):
@@ -321,8 +319,6 @@
     New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onBind(android.content.Intent)
 UnflaggedApi: android.media.midi.MidiUmpDeviceService#onClose():
     New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onClose()
-UnflaggedApi: android.media.midi.MidiUmpDeviceService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onCreate()
 UnflaggedApi: android.media.midi.MidiUmpDeviceService#onDeviceStatusChanged(android.media.midi.MidiDeviceStatus):
     New API must be flagged with @FlaggedApi: method android.media.midi.MidiUmpDeviceService.onDeviceStatusChanged(android.media.midi.MidiDeviceStatus)
 UnflaggedApi: android.media.midi.MidiUmpDeviceService#onGetInputPortReceivers():
@@ -335,8 +331,6 @@
     New API must be flagged with @FlaggedApi: field android.os.UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO
 UnflaggedApi: android.provider.Settings#ACTION_CREDENTIAL_PROVIDER:
     New API must be flagged with @FlaggedApi: field android.provider.Settings.ACTION_CREDENTIAL_PROVIDER
-UnflaggedApi: android.telecom.Call.Details#PROPERTY_IS_TRANSACTIONAL:
-    New API must be flagged with @FlaggedApi: field android.telecom.Call.Details.PROPERTY_IS_TRANSACTIONAL
 UnflaggedApi: android.telecom.Call.Details#getId():
     New API must be flagged with @FlaggedApi: method android.telecom.Call.Details.getId()
 UnflaggedApi: android.telephony.CarrierConfigManager#KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE:
@@ -355,46 +349,10 @@
     New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE
 UnflaggedApi: android.telephony.TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED:
     New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED
-UnflaggedApi: android.text.BoringLayout#computeDrawingBoundingBox():
-    New API must be flagged with @FlaggedApi: method android.text.BoringLayout.computeDrawingBoundingBox()
-UnflaggedApi: android.text.BoringLayout.Metrics#getDrawingBoundingBox():
-    New API must be flagged with @FlaggedApi: method android.text.BoringLayout.Metrics.getDrawingBoundingBox()
-UnflaggedApi: android.text.DynamicLayout#getLineBreakConfig():
-    New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.getLineBreakConfig()
 UnflaggedApi: android.text.DynamicLayout.Builder#setLineBreakConfig(android.graphics.text.LineBreakConfig):
     New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.Builder.setLineBreakConfig(android.graphics.text.LineBreakConfig)
 UnflaggedApi: android.text.DynamicLayout.Builder#setUseBoundsForWidth(boolean):
     New API must be flagged with @FlaggedApi: method android.text.DynamicLayout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.Layout#computeDrawingBoundingBox():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.computeDrawingBoundingBox()
-UnflaggedApi: android.text.Layout#getBreakStrategy():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getBreakStrategy()
-UnflaggedApi: android.text.Layout#getEllipsize():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getEllipsize()
-UnflaggedApi: android.text.Layout#getHyphenationFrequency():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getHyphenationFrequency()
-UnflaggedApi: android.text.Layout#getJustificationMode():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getJustificationMode()
-UnflaggedApi: android.text.Layout#getLeftIndents():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getLeftIndents()
-UnflaggedApi: android.text.Layout#getLineBreakConfig():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getLineBreakConfig()
-UnflaggedApi: android.text.Layout#getLineSpacingAmount():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getLineSpacingAmount()
-UnflaggedApi: android.text.Layout#getLineSpacingMultiplier():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getLineSpacingMultiplier()
-UnflaggedApi: android.text.Layout#getMaxLines():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getMaxLines()
-UnflaggedApi: android.text.Layout#getRightIndents():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getRightIndents()
-UnflaggedApi: android.text.Layout#getTextDirectionHeuristic():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getTextDirectionHeuristic()
-UnflaggedApi: android.text.Layout#getUseBoundsForWidth():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.getUseBoundsForWidth()
-UnflaggedApi: android.text.Layout#isFontPaddingIncluded():
-    New API must be flagged with @FlaggedApi: method android.text.Layout.isFontPaddingIncluded()
-UnflaggedApi: android.text.Layout.Builder:
-    New API must be flagged with @FlaggedApi: class android.text.Layout.Builder
 UnflaggedApi: android.text.Layout.Builder#Builder(CharSequence, int, int, android.text.TextPaint, int):
     New API must be flagged with @FlaggedApi: constructor android.text.Layout.Builder(CharSequence,int,int,android.text.TextPaint,int)
 UnflaggedApi: android.text.Layout.Builder#build():
@@ -429,24 +387,12 @@
     New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setRightIndents(int[])
 UnflaggedApi: android.text.Layout.Builder#setTextDirectionHeuristic(android.text.TextDirectionHeuristic):
     New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setTextDirectionHeuristic(android.text.TextDirectionHeuristic)
-UnflaggedApi: android.text.Layout.Builder#setUseBoundsForWidth(boolean):
-    New API must be flagged with @FlaggedApi: method android.text.Layout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.StaticLayout#computeDrawingBoundingBox():
-    New API must be flagged with @FlaggedApi: method android.text.StaticLayout.computeDrawingBoundingBox()
 UnflaggedApi: android.text.StaticLayout.Builder#setUseBoundsForWidth(boolean):
     New API must be flagged with @FlaggedApi: method android.text.StaticLayout.Builder.setUseBoundsForWidth(boolean)
-UnflaggedApi: android.text.style.LineBreakConfigSpan:
-    New API must be flagged with @FlaggedApi: class android.text.style.LineBreakConfigSpan
 UnflaggedApi: android.text.style.LineBreakConfigSpan#LineBreakConfigSpan(android.graphics.text.LineBreakConfig):
     New API must be flagged with @FlaggedApi: constructor android.text.style.LineBreakConfigSpan(android.graphics.text.LineBreakConfig)
-UnflaggedApi: android.text.style.LineBreakConfigSpan#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.equals(Object)
 UnflaggedApi: android.text.style.LineBreakConfigSpan#getLineBreakConfig():
     New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.getLineBreakConfig()
-UnflaggedApi: android.text.style.LineBreakConfigSpan#hashCode():
-    New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.hashCode()
-UnflaggedApi: android.text.style.LineBreakConfigSpan#toString():
-    New API must be flagged with @FlaggedApi: method android.text.style.LineBreakConfigSpan.toString()
 UnflaggedApi: android.view.HapticScrollFeedbackProvider#HapticScrollFeedbackProvider(android.view.View):
     New API must be flagged with @FlaggedApi: constructor android.view.HapticScrollFeedbackProvider(android.view.View)
 UnflaggedApi: android.view.HapticScrollFeedbackProvider#onScrollLimit(int, int, int, boolean):
@@ -455,16 +401,10 @@
     New API must be flagged with @FlaggedApi: method android.view.HapticScrollFeedbackProvider.onScrollProgress(int,int,int,int)
 UnflaggedApi: android.view.HapticScrollFeedbackProvider#onSnapToItem(int, int, int):
     New API must be flagged with @FlaggedApi: method android.view.HapticScrollFeedbackProvider.onSnapToItem(int,int,int)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollLimit(android.view.MotionEvent, int, boolean):
-    New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollLimit(android.view.MotionEvent,int,boolean)
 UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollLimit(int, int, int, boolean):
     New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollLimit(int,int,int,boolean)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollProgress(android.view.MotionEvent, int, int):
-    New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollProgress(android.view.MotionEvent,int,int)
 UnflaggedApi: android.view.ScrollFeedbackProvider#onScrollProgress(int, int, int, int):
     New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onScrollProgress(int,int,int,int)
-UnflaggedApi: android.view.ScrollFeedbackProvider#onSnapToItem(android.view.MotionEvent, int):
-    New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onSnapToItem(android.view.MotionEvent,int)
 UnflaggedApi: android.view.ScrollFeedbackProvider#onSnapToItem(int, int, int):
     New API must be flagged with @FlaggedApi: method android.view.ScrollFeedbackProvider.onSnapToItem(int,int,int)
 UnflaggedApi: android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT:
@@ -515,7 +455,3 @@
     New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setClientSupported(boolean)
 UnflaggedApi: android.view.inputmethod.InlineSuggestionsRequest.Builder#setServiceSupported(boolean):
     New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setServiceSupported(boolean)
-UnflaggedApi: android.widget.TextView#getUseBoundsForWidth():
-    New API must be flagged with @FlaggedApi: method android.widget.TextView.getUseBoundsForWidth()
-UnflaggedApi: android.widget.TextView#setUseBoundsForWidth(boolean):
-    New API must be flagged with @FlaggedApi: method android.widget.TextView.setUseBoundsForWidth(boolean)
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index a0d3858..1633835 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -35,26 +35,8 @@
     SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnMediaKeyListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.media.session.MediaSessionManager#setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler):
     SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnVolumeKeyLongPressListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
-    SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
-    SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.Binder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
-    SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipient, int):
-    SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
-    SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 
 
-UnflaggedApi: android.Manifest.permission#BLUETOOTH_STACK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BLUETOOTH_STACK
-UnflaggedApi: android.Manifest.permission#CONTROL_AUTOMOTIVE_GNSS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS
-UnflaggedApi: android.Manifest.permission#GET_INTENT_SENDER_INTENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_INTENT_SENDER_INTENT
-UnflaggedApi: android.Manifest.permission#MAKE_UID_VISIBLE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MAKE_UID_VISIBLE
 UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
 UnflaggedApi: android.Manifest.permission#USE_COMPANION_TRANSPORTS:
@@ -89,13 +71,7 @@
     New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.OnTransportsChangedListener.onTransportsChanged(java.util.List<android.companion.AssociationInfo>)
 UnflaggedApi: android.content.Context#REMOTE_AUTH_SERVICE:
     New API must be flagged with @FlaggedApi: field android.content.Context.REMOTE_AUTH_SERVICE
-UnflaggedApi: android.content.ContextWrapper#createContextForSdkInSandbox(android.content.pm.ApplicationInfo, int):
-    New API must be flagged with @FlaggedApi: method android.content.ContextWrapper.createContextForSdkInSandbox(android.content.pm.ApplicationInfo,int)
-UnflaggedApi: android.media.session.MediaController.PlaybackInfo#PlaybackInfo(int, int, int, int, android.media.AudioAttributes, String):
-    New API must be flagged with @FlaggedApi: constructor android.media.session.MediaController.PlaybackInfo(int,int,int,int,android.media.AudioAttributes,String)
 UnflaggedApi: android.os.IpcDataCache#MODULE_TELEPHONY:
     New API must be flagged with @FlaggedApi: field android.os.IpcDataCache.MODULE_TELEPHONY
-UnflaggedApi: android.provider.ContactsContract.RawContactsEntity#queryRawContactEntity(android.content.ContentResolver, long):
-    New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.RawContactsEntity.queryRawContactEntity(android.content.ContentResolver,long)
 UnflaggedApi: android.provider.Settings.Config#getAllStrings():
     New API must be flagged with @FlaggedApi: method android.provider.Settings.Config.getAllStrings()
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fcfa41a6..7c78f09 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -95,6 +95,7 @@
     field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
     field public static final String CALL_AUDIO_INTERCEPTION = "android.permission.CALL_AUDIO_INTERCEPTION";
     field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
+    field public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
     field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
     field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
     field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
@@ -3483,6 +3484,7 @@
     field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final String TETHERING_SERVICE = "tethering";
+    field public static final String THREAD_NETWORK_SERVICE = "thread_network";
     field public static final String TIME_MANAGER_SERVICE = "time_manager";
     field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
     field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -3798,10 +3800,6 @@
     field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
   }
 
-  public class PackageInfo implements android.os.Parcelable {
-    field public boolean isArchived;
-  }
-
   public class PackageInstaller {
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
@@ -3879,6 +3877,7 @@
     method public static void forceSafeLabels();
     method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
     method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
+    field @FlaggedApi(Flags.FLAG_ARCHIVING) public boolean isArchived;
   }
 
   public abstract class PackageManager {
@@ -16707,10 +16706,13 @@
     field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
     field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
     field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
+    field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
+    field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
     field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
     field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
     field public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
     field public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
+    field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
     field public static final int SATELLITE_MODEM_STATE_OFF = 4; // 0x4
     field public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
     field public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index d62bea8..ee031db 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -21,28 +21,10 @@
     Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
 
 
-MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
-    android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
-MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
-    android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
-
-
 MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0:
     Missing nullability on parameter `intent` in method `onUnbind`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #0:
-    Missing nullability on parameter `inputId` in method `onEvent`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
-    Missing nullability on parameter `eventType` in method `onEvent`
-MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
-    Missing nullability on parameter `eventArgs` in method `onEvent`
 MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
     Missing nullability on parameter `base` in method `attachBaseContext`
-MissingNullability: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
-    Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSync`
-MissingNullability: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
-    Missing nullability on field `METADATA_AUTHORITY_URI` in class `class android.provider.ContactsContract.MetadataSync`
-MissingNullability: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
-    Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSyncState`
 MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0:
     Missing nullability on parameter `context` in method `attachInfo`
 MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1:
@@ -61,10 +43,6 @@
     Missing nullability on parameter `intent` in method `onUnbind`
 MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
     Missing nullability on parameter `intent` in method `onUnbind`
-MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
-    Missing nullability on method `setServiceId` return
-MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0:
-    Missing nullability on parameter `serviceId` in method `setServiceId`
 
 
 ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
@@ -173,12 +151,6 @@
     SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler):
     SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity):
-    SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setBeamPushUrisCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...):
-    SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setNdefPushMessageCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...):
-    SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setOnNdefPushCompleteCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
     SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
@@ -225,1350 +197,32 @@
     SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 
 
-UnflaggedApi: android.Manifest.permission#ACCESS_AMBIENT_CONTEXT_EVENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT
-UnflaggedApi: android.Manifest.permission#ACCESS_AMBIENT_LIGHT_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_BROADCAST_RADIO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_BROADCAST_RADIO
-UnflaggedApi: android.Manifest.permission#ACCESS_BROADCAST_RESPONSE_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_CACHE_FILESYSTEM:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_CACHE_FILESYSTEM
-UnflaggedApi: android.Manifest.permission#ACCESS_CONTEXT_HUB:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_CONTEXT_HUB
-UnflaggedApi: android.Manifest.permission#ACCESS_DRM_CERTIFICATES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_DRM_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#ACCESS_FPS_COUNTER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_FPS_COUNTER
-UnflaggedApi: android.Manifest.permission#ACCESS_INSTANT_APPS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_INSTANT_APPS
-UnflaggedApi: android.Manifest.permission#ACCESS_LOCUS_ID_USAGE_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_LOCUS_ID_USAGE_STATS
-UnflaggedApi: android.Manifest.permission#ACCESS_MOCK_LOCATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_MOCK_LOCATION
-UnflaggedApi: android.Manifest.permission#ACCESS_MTP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_MTP
-UnflaggedApi: android.Manifest.permission#ACCESS_NETWORK_CONDITIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_NETWORK_CONDITIONS
-UnflaggedApi: android.Manifest.permission#ACCESS_NOTIFICATIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#ACCESS_PDB_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_PDB_STATE
-UnflaggedApi: android.Manifest.permission#ACCESS_RCS_USER_CAPABILITY_EXCHANGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE
-UnflaggedApi: android.Manifest.permission#ACCESS_SHARED_LIBRARIES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SHARED_LIBRARIES
-UnflaggedApi: android.Manifest.permission#ACCESS_SHORTCUTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SHORTCUTS
 UnflaggedApi: android.Manifest.permission#ACCESS_SMARTSPACE:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SMARTSPACE
-UnflaggedApi: android.Manifest.permission#ACCESS_SURFACE_FLINGER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SURFACE_FLINGER
-UnflaggedApi: android.Manifest.permission#ACCESS_TUNED_INFO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TUNED_INFO
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_DESCRAMBLER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_DESCRAMBLER
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_SHARED_FILTER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_SHARED_FILTER
-UnflaggedApi: android.Manifest.permission#ACCESS_TV_TUNER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_TV_TUNER
-UnflaggedApi: android.Manifest.permission#ACCESS_ULTRASOUND:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_ULTRASOUND
-UnflaggedApi: android.Manifest.permission#ACCESS_VIBRATOR_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_VIBRATOR_STATE
-UnflaggedApi: android.Manifest.permission#ACTIVITY_EMBEDDING:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACTIVITY_EMBEDDING
-UnflaggedApi: android.Manifest.permission#ADD_ALWAYS_UNLOCKED_DISPLAY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY
-UnflaggedApi: android.Manifest.permission#ADD_TRUSTED_DISPLAY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADD_TRUSTED_DISPLAY
-UnflaggedApi: android.Manifest.permission#ADJUST_RUNTIME_PERMISSIONS_POLICY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
-UnflaggedApi: android.Manifest.permission#ALLOCATE_AGGRESSIVE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOCATE_AGGRESSIVE
-UnflaggedApi: android.Manifest.permission#ALLOW_ANY_CODEC_FOR_PLAYBACK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK
-UnflaggedApi: android.Manifest.permission#ALLOW_PLACE_IN_MULTI_PANE_SETTINGS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS
-UnflaggedApi: android.Manifest.permission#ALLOW_SLIPPERY_TOUCHES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES
 UnflaggedApi: android.Manifest.permission#ALWAYS_UPDATE_WALLPAPER:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER
-UnflaggedApi: android.Manifest.permission#AMBIENT_WALLPAPER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.AMBIENT_WALLPAPER
-UnflaggedApi: android.Manifest.permission#APPROVE_INCIDENT_REPORTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.APPROVE_INCIDENT_REPORTS
-UnflaggedApi: android.Manifest.permission#ASSOCIATE_COMPANION_DEVICES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES
-UnflaggedApi: android.Manifest.permission#BACKGROUND_CAMERA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BACKGROUND_CAMERA
-UnflaggedApi: android.Manifest.permission#BACKUP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BACKUP
-UnflaggedApi: android.Manifest.permission#BATTERY_PREDICTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BATTERY_PREDICTION
-UnflaggedApi: android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_ATTENTION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_ATTENTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_AUGMENTED_AUTOFILL_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_AUGMENTED_AUTOFILL_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CALL_DIAGNOSTIC_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CALL_DIAGNOSTIC_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CALL_STREAMING_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CALL_STREAMING_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CELL_BROADCAST_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_CONTENT_SUGGESTIONS_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_CONTENT_SUGGESTIONS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_DIRECTORY_SEARCH:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DIRECTORY_SEARCH
-UnflaggedApi: android.Manifest.permission#BIND_DISPLAY_HASHING_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DISPLAY_HASHING_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_DOMAIN_VERIFICATION_AGENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_DOMAIN_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#BIND_EUICC_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_EUICC_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_FIELD_CLASSIFICATION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_FIELD_CLASSIFICATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_GBA_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_GBA_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_HOTWORD_DETECTION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_IMS_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_IMS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_KEYGUARD_APPWIDGET:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_KEYGUARD_APPWIDGET
-UnflaggedApi: android.Manifest.permission#BIND_MUSIC_RECOGNITION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_NETWORK_RECOMMENDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_PRINT_RECOMMENDATION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_PRINT_RECOMMENDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RESOLVER_RANKER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_ROTATION_RESOLVER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_ROTATION_RESOLVER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SATELLITE_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SATELLITE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SETTINGS_SUGGESTIONS_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_SOUND_TRIGGER_DETECTION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TELEPHONY_DATA_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TELEPHONY_DATA_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TELEPHONY_NETWORK_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TELEPHONY_NETWORK_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRACE_REPORT_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRACE_REPORT_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRANSLATION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRANSLATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_TRUST_AGENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TRUST_AGENT
-UnflaggedApi: android.Manifest.permission#BIND_TV_REMOTE_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_TV_REMOTE_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_VISUAL_QUERY_DETECTION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE
-UnflaggedApi: android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BIND_WEARABLE_SENSING_SERVICE
-UnflaggedApi: android.Manifest.permission#BLUETOOTH_MAP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BLUETOOTH_MAP
-UnflaggedApi: android.Manifest.permission#BRICK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BRICK
-UnflaggedApi: android.Manifest.permission#BRIGHTNESS_SLIDER_USAGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE
-UnflaggedApi: android.Manifest.permission#BROADCAST_CLOSE_SYSTEM_DIALOGS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
-UnflaggedApi: android.Manifest.permission#BYPASS_ROLE_QUALIFICATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.BYPASS_ROLE_QUALIFICATION
-UnflaggedApi: android.Manifest.permission#CALL_AUDIO_INTERCEPTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CALL_AUDIO_INTERCEPTION
-UnflaggedApi: android.Manifest.permission#CAMERA_DISABLE_TRANSMIT_LED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_DISABLE_TRANSMIT_LED
-UnflaggedApi: android.Manifest.permission#CAMERA_OPEN_CLOSE_LISTENER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER
-UnflaggedApi: android.Manifest.permission#CAPTURE_AUDIO_HOTWORD:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
-UnflaggedApi: android.Manifest.permission#CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD
-UnflaggedApi: android.Manifest.permission#CAPTURE_MEDIA_OUTPUT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_MEDIA_OUTPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_TUNER_AUDIO_INPUT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_TV_INPUT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_TV_INPUT
-UnflaggedApi: android.Manifest.permission#CAPTURE_VOICE_COMMUNICATION_OUTPUT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT
-UnflaggedApi: android.Manifest.permission#CHANGE_APP_IDLE_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_APP_IDLE_STATE
-UnflaggedApi: android.Manifest.permission#CHANGE_APP_LAUNCH_TIME_ESTIMATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE
-UnflaggedApi: android.Manifest.permission#CHANGE_DEVICE_IDLE_TEMP_WHITELIST:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST
-UnflaggedApi: android.Manifest.permission#CHECK_REMOTE_LOCKSCREEN:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN
-UnflaggedApi: android.Manifest.permission#CLEAR_APP_USER_DATA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CLEAR_APP_USER_DATA
-UnflaggedApi: android.Manifest.permission#COMPANION_APPROVE_WIFI_CONNECTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS
-UnflaggedApi: android.Manifest.permission#CONFIGURE_DISPLAY_BRIGHTNESS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS
-UnflaggedApi: android.Manifest.permission#CONFIGURE_INTERACT_ACROSS_PROFILES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES
-UnflaggedApi: android.Manifest.permission#CONNECTIVITY_USE_RESTRICTED_NETWORKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
-UnflaggedApi: android.Manifest.permission#CONTROL_DEVICE_LIGHTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DEVICE_LIGHTS
-UnflaggedApi: android.Manifest.permission#CONTROL_DISPLAY_COLOR_TRANSFORMS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS
-UnflaggedApi: android.Manifest.permission#CONTROL_DISPLAY_SATURATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_DISPLAY_SATURATION
-UnflaggedApi: android.Manifest.permission#CONTROL_INCALL_EXPERIENCE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_INCALL_EXPERIENCE
-UnflaggedApi: android.Manifest.permission#CONTROL_KEYGUARD_SECURE_NOTIFICATIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#CONTROL_OEM_PAID_NETWORK_PREFERENCE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE
-UnflaggedApi: android.Manifest.permission#CONTROL_VPN:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CONTROL_VPN
-UnflaggedApi: android.Manifest.permission#CREATE_USERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CREATE_USERS
-UnflaggedApi: android.Manifest.permission#CREATE_VIRTUAL_DEVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CREATE_VIRTUAL_DEVICE
-UnflaggedApi: android.Manifest.permission#CRYPT_KEEPER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CRYPT_KEEPER
-UnflaggedApi: android.Manifest.permission#DEVICE_POWER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.DEVICE_POWER
-UnflaggedApi: android.Manifest.permission#DISABLE_SYSTEM_SOUND_EFFECTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS
-UnflaggedApi: android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE
-UnflaggedApi: android.Manifest.permission#DOMAIN_VERIFICATION_AGENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#ENTER_CAR_MODE_PRIORITIZED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED
-UnflaggedApi: android.Manifest.permission#EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS
-UnflaggedApi: android.Manifest.permission#FORCE_BACK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.FORCE_BACK
-UnflaggedApi: android.Manifest.permission#FORCE_STOP_PACKAGES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.FORCE_STOP_PACKAGES
-UnflaggedApi: android.Manifest.permission#GET_APP_METADATA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_APP_METADATA
-UnflaggedApi: android.Manifest.permission#GET_APP_OPS_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#GET_HISTORICAL_APP_OPS_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_HISTORICAL_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#GET_PROCESS_STATE_AND_OOM_SCORE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_PROCESS_STATE_AND_OOM_SCORE
-UnflaggedApi: android.Manifest.permission#GET_RUNTIME_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#GET_TOP_ACTIVITY_INFO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GET_TOP_ACTIVITY_INFO
-UnflaggedApi: android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS
-UnflaggedApi: android.Manifest.permission#HANDLE_CAR_MODE_CHANGES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.HANDLE_CAR_MODE_CHANGES
-UnflaggedApi: android.Manifest.permission#HARDWARE_TEST:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.HARDWARE_TEST
-UnflaggedApi: android.Manifest.permission#HDMI_CEC:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.HDMI_CEC
-UnflaggedApi: android.Manifest.permission#INJECT_EVENTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INJECT_EVENTS
-UnflaggedApi: android.Manifest.permission#INSTALL_DPC_PACKAGES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_DPC_PACKAGES
-UnflaggedApi: android.Manifest.permission#INSTALL_DYNAMIC_SYSTEM:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM
-UnflaggedApi: android.Manifest.permission#INSTALL_EXISTING_PACKAGES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_EXISTING_PACKAGES
-UnflaggedApi: android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE
-UnflaggedApi: android.Manifest.permission#INSTALL_PACKAGE_UPDATES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_PACKAGE_UPDATES
-UnflaggedApi: android.Manifest.permission#INSTALL_SELF_UPDATES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INSTALL_SELF_UPDATES
-UnflaggedApi: android.Manifest.permission#INTENT_FILTER_VERIFICATION_AGENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#INTERACT_ACROSS_USERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERACT_ACROSS_USERS
-UnflaggedApi: android.Manifest.permission#INTERACT_ACROSS_USERS_FULL:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
-UnflaggedApi: android.Manifest.permission#INTERNAL_SYSTEM_WINDOW:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INTERNAL_SYSTEM_WINDOW
-UnflaggedApi: android.Manifest.permission#INVOKE_CARRIER_SETUP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.INVOKE_CARRIER_SETUP
-UnflaggedApi: android.Manifest.permission#KILL_ALL_BACKGROUND_PROCESSES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.KILL_ALL_BACKGROUND_PROCESSES
-UnflaggedApi: android.Manifest.permission#KILL_UID:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.KILL_UID
-UnflaggedApi: android.Manifest.permission#LAUNCH_DEVICE_MANAGER_SETUP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP
+UnflaggedApi: android.Manifest.permission#CAMERA_HEADLESS_SYSTEM_USER:
+    New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_HEADLESS_SYSTEM_USER
 UnflaggedApi: android.Manifest.permission#LAUNCH_PERMISSION_SETTINGS:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_PERMISSION_SETTINGS
-UnflaggedApi: android.Manifest.permission#LOCAL_MAC_ADDRESS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCAL_MAC_ADDRESS
-UnflaggedApi: android.Manifest.permission#LOCATION_BYPASS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCATION_BYPASS
-UnflaggedApi: android.Manifest.permission#LOCK_DEVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOCK_DEVICE
-UnflaggedApi: android.Manifest.permission#LOG_FOREGROUND_RESOURCE_USE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOG_FOREGROUND_RESOURCE_USE
-UnflaggedApi: android.Manifest.permission#LOOP_RADIO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.LOOP_RADIO
-UnflaggedApi: android.Manifest.permission#MANAGE_ACCESSIBILITY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ACCESSIBILITY
-UnflaggedApi: android.Manifest.permission#MANAGE_ACTIVITY_TASKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ACTIVITY_TASKS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_HIBERNATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_HIBERNATION
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_OPS_RESTRICTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_PREDICTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_PREDICTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_APP_TOKENS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_APP_TOKENS
-UnflaggedApi: android.Manifest.permission#MANAGE_AUTO_FILL:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_AUTO_FILL
-UnflaggedApi: android.Manifest.permission#MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED
-UnflaggedApi: android.Manifest.permission#MANAGE_CARRIER_OEM_UNLOCK_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#MANAGE_CA_CERTIFICATES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CA_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#MANAGE_CLIPBOARD_ACCESS_NOTIFICATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION
-UnflaggedApi: android.Manifest.permission#MANAGE_CLOUDSEARCH:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CLOUDSEARCH
-UnflaggedApi: android.Manifest.permission#MANAGE_CONTENT_CAPTURE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CONTENT_CAPTURE
-UnflaggedApi: android.Manifest.permission#MANAGE_CONTENT_SUGGESTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEBUGGING:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEBUGGING
-UnflaggedApi: android.Manifest.permission#MANAGE_DEFAULT_APPLICATIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEVICE_ADMINS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEVICE_ADMINS
-UnflaggedApi: android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_EXEMPTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_ETHERNET_NETWORKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ETHERNET_NETWORKS
-UnflaggedApi: android.Manifest.permission#MANAGE_FACTORY_RESET_PROTECTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_GAME_ACTIVITY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_GAME_ACTIVITY
-UnflaggedApi: android.Manifest.permission#MANAGE_GAME_MODE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_GAME_MODE
-UnflaggedApi: android.Manifest.permission#MANAGE_HOTWORD_DETECTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_HOTWORD_DETECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_IPSEC_TUNNELS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_IPSEC_TUNNELS
-UnflaggedApi: android.Manifest.permission#MANAGE_LOW_POWER_STANDBY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_LOW_POWER_STANDBY
-UnflaggedApi: android.Manifest.permission#MANAGE_MUSIC_RECOGNITION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_MUSIC_RECOGNITION
-UnflaggedApi: android.Manifest.permission#MANAGE_NOTIFICATION_LISTENERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ONE_TIME_PERMISSION_SESSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
-UnflaggedApi: android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROLE_HOLDERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROLE_HOLDERS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROLLBACKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROLLBACKS
-UnflaggedApi: android.Manifest.permission#MANAGE_ROTATION_RESOLVER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_ROTATION_RESOLVER
-UnflaggedApi: android.Manifest.permission#MANAGE_SAFETY_CENTER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SAFETY_CENTER
-UnflaggedApi: android.Manifest.permission#MANAGE_SEARCH_UI:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SEARCH_UI
-UnflaggedApi: android.Manifest.permission#MANAGE_SENSOR_PRIVACY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SENSOR_PRIVACY
-UnflaggedApi: android.Manifest.permission#MANAGE_SMARTSPACE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SMARTSPACE
-UnflaggedApi: android.Manifest.permission#MANAGE_SOUND_TRIGGER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SOUND_TRIGGER
-UnflaggedApi: android.Manifest.permission#MANAGE_SPEECH_RECOGNITION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SPEECH_RECOGNITION
-UnflaggedApi: android.Manifest.permission#MANAGE_SUBSCRIPTION_PLANS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS
-UnflaggedApi: android.Manifest.permission#MANAGE_SUBSCRIPTION_USER_ASSOCIATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION
-UnflaggedApi: android.Manifest.permission#MANAGE_TEST_NETWORKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_TEST_NETWORKS
-UnflaggedApi: android.Manifest.permission#MANAGE_TIME_AND_ZONE_DETECTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION
-UnflaggedApi: android.Manifest.permission#MANAGE_UI_TRANSLATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_UI_TRANSLATION
-UnflaggedApi: android.Manifest.permission#MANAGE_USB:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USB
-UnflaggedApi: android.Manifest.permission#MANAGE_USERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USERS
-UnflaggedApi: android.Manifest.permission#MANAGE_USER_OEM_UNLOCK_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION
-UnflaggedApi: android.Manifest.permission#MANAGE_WEAK_ESCROW_TOKEN:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN
-UnflaggedApi: android.Manifest.permission#MANAGE_WEARABLE_SENSING_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE
-UnflaggedApi: android.Manifest.permission#MANAGE_WIFI_COUNTRY_CODE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE
-UnflaggedApi: android.Manifest.permission#MARK_DEVICE_ORGANIZATION_OWNED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED
-UnflaggedApi: android.Manifest.permission#MEDIA_RESOURCE_OVERRIDE_PID:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID
-UnflaggedApi: android.Manifest.permission#MIGRATE_HEALTH_CONNECT_DATA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA
-UnflaggedApi: android.Manifest.permission#MODIFY_APPWIDGET_BIND_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#MODIFY_AUDIO_ROUTING:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_AUDIO_ROUTING
-UnflaggedApi: android.Manifest.permission#MODIFY_AUDIO_SETTINGS_PRIVILEGED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#MODIFY_CELL_BROADCASTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_CELL_BROADCASTS
-UnflaggedApi: android.Manifest.permission#MODIFY_DAY_NIGHT_MODE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_DAY_NIGHT_MODE
-UnflaggedApi: android.Manifest.permission#MODIFY_PARENTAL_CONTROLS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_PARENTAL_CONTROLS
-UnflaggedApi: android.Manifest.permission#MODIFY_QUIET_MODE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_QUIET_MODE
-UnflaggedApi: android.Manifest.permission#MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE
-UnflaggedApi: android.Manifest.permission#MONITOR_DEVICE_CONFIG_ACCESS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS
-UnflaggedApi: android.Manifest.permission#MOVE_PACKAGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MOVE_PACKAGE
-UnflaggedApi: android.Manifest.permission#NETWORK_AIRPLANE_MODE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_AIRPLANE_MODE
-UnflaggedApi: android.Manifest.permission#NETWORK_CARRIER_PROVISIONING:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_CARRIER_PROVISIONING
-UnflaggedApi: android.Manifest.permission#NETWORK_FACTORY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_FACTORY
-UnflaggedApi: android.Manifest.permission#NETWORK_MANAGED_PROVISIONING:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_MANAGED_PROVISIONING
-UnflaggedApi: android.Manifest.permission#NETWORK_SCAN:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SCAN
-UnflaggedApi: android.Manifest.permission#NETWORK_SETTINGS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SETTINGS
-UnflaggedApi: android.Manifest.permission#NETWORK_SETUP_WIZARD:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SETUP_WIZARD
-UnflaggedApi: android.Manifest.permission#NETWORK_SIGNAL_STRENGTH_WAKEUP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP
-UnflaggedApi: android.Manifest.permission#NETWORK_STACK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_STACK
-UnflaggedApi: android.Manifest.permission#NETWORK_STATS_PROVIDER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NETWORK_STATS_PROVIDER
-UnflaggedApi: android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON
-UnflaggedApi: android.Manifest.permission#NOTIFICATION_DURING_SETUP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NOTIFICATION_DURING_SETUP
-UnflaggedApi: android.Manifest.permission#NOTIFY_TV_INPUTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.NOTIFY_TV_INPUTS
-UnflaggedApi: android.Manifest.permission#OBSERVE_APP_USAGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_APP_USAGE
-UnflaggedApi: android.Manifest.permission#OBSERVE_NETWORK_POLICY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_NETWORK_POLICY
-UnflaggedApi: android.Manifest.permission#OBSERVE_ROLE_HOLDERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_ROLE_HOLDERS
-UnflaggedApi: android.Manifest.permission#OBSERVE_SENSOR_PRIVACY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.OBSERVE_SENSOR_PRIVACY
-UnflaggedApi: android.Manifest.permission#OPEN_ACCESSIBILITY_DETAILS_SETTINGS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS
-UnflaggedApi: android.Manifest.permission#OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD
-UnflaggedApi: android.Manifest.permission#PACKAGE_VERIFICATION_AGENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PACKAGE_VERIFICATION_AGENT
-UnflaggedApi: android.Manifest.permission#PACKET_KEEPALIVE_OFFLOAD:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
-UnflaggedApi: android.Manifest.permission#PEERS_MAC_ADDRESS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PEERS_MAC_ADDRESS
-UnflaggedApi: android.Manifest.permission#PERFORM_CDMA_PROVISIONING:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_CDMA_PROVISIONING
-UnflaggedApi: android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION
-UnflaggedApi: android.Manifest.permission#PERFORM_SIM_ACTIVATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PERFORM_SIM_ACTIVATION
-UnflaggedApi: android.Manifest.permission#POWER_SAVER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.POWER_SAVER
-UnflaggedApi: android.Manifest.permission#PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE
-UnflaggedApi: android.Manifest.permission#PROVIDE_RESOLVER_RANKER_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_RESOLVER_RANKER_SERVICE
-UnflaggedApi: android.Manifest.permission#PROVIDE_TRUST_AGENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVIDE_TRUST_AGENT
-UnflaggedApi: android.Manifest.permission#PROVISION_DEMO_DEVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.PROVISION_DEMO_DEVICE
-UnflaggedApi: android.Manifest.permission#QUERY_ADMIN_POLICY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_ADMIN_POLICY
-UnflaggedApi: android.Manifest.permission#QUERY_CLONED_APPS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_CLONED_APPS
-UnflaggedApi: android.Manifest.permission#QUERY_USERS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.QUERY_USERS
-UnflaggedApi: android.Manifest.permission#RADIO_SCAN_WITHOUT_LOCATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION
-UnflaggedApi: android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION
-UnflaggedApi: android.Manifest.permission#READ_APP_SPECIFIC_LOCALES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_APP_SPECIFIC_LOCALES
-UnflaggedApi: android.Manifest.permission#READ_CARRIER_APP_INFO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CARRIER_APP_INFO
-UnflaggedApi: android.Manifest.permission#READ_CELL_BROADCASTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CELL_BROADCASTS
-UnflaggedApi: android.Manifest.permission#READ_CLIPBOARD_IN_BACKGROUND:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND
-UnflaggedApi: android.Manifest.permission#READ_CONTENT_RATING_SYSTEMS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS
-UnflaggedApi: android.Manifest.permission#READ_DEVICE_CONFIG:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#READ_DREAM_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_DREAM_STATE
-UnflaggedApi: android.Manifest.permission#READ_GLOBAL_APP_SEARCH_DATA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA
 UnflaggedApi: android.Manifest.permission#READ_INSTALLED_SESSION_PATHS:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALLED_SESSION_PATHS
-UnflaggedApi: android.Manifest.permission#READ_INSTALL_SESSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALL_SESSIONS
-UnflaggedApi: android.Manifest.permission#READ_NETWORK_USAGE_HISTORY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_NETWORK_USAGE_HISTORY
-UnflaggedApi: android.Manifest.permission#READ_OEM_UNLOCK_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_OEM_UNLOCK_STATE
-UnflaggedApi: android.Manifest.permission#READ_PEOPLE_DATA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PEOPLE_DATA
-UnflaggedApi: android.Manifest.permission#READ_PRINT_SERVICES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRINT_SERVICES
-UnflaggedApi: android.Manifest.permission#READ_PRINT_SERVICE_RECOMMENDATIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS
-UnflaggedApi: android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
-UnflaggedApi: android.Manifest.permission#READ_PROJECTION_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_PROJECTION_STATE
-UnflaggedApi: android.Manifest.permission#READ_RESTRICTED_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_RESTRICTED_STATS
-UnflaggedApi: android.Manifest.permission#READ_RUNTIME_PROFILES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_RUNTIME_PROFILES
-UnflaggedApi: android.Manifest.permission#READ_SAFETY_CENTER_STATUS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SAFETY_CENTER_STATUS
-UnflaggedApi: android.Manifest.permission#READ_SEARCH_INDEXABLES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SEARCH_INDEXABLES
-UnflaggedApi: android.Manifest.permission#READ_SYSTEM_UPDATE_INFO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_SYSTEM_UPDATE_INFO
-UnflaggedApi: android.Manifest.permission#READ_WALLPAPER_INTERNAL:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WALLPAPER_INTERNAL
-UnflaggedApi: android.Manifest.permission#READ_WIFI_CREDENTIAL:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WIFI_CREDENTIAL
-UnflaggedApi: android.Manifest.permission#READ_WRITE_SYNC_DISABLED_MODE_CONFIG:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG
-UnflaggedApi: android.Manifest.permission#REAL_GET_TASKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REAL_GET_TASKS
-UnflaggedApi: android.Manifest.permission#RECEIVE_BLUETOOTH_MAP:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_BLUETOOTH_MAP
-UnflaggedApi: android.Manifest.permission#RECEIVE_DATA_ACTIVITY_CHANGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE
-UnflaggedApi: android.Manifest.permission#RECEIVE_DEVICE_CUSTOMIZATION_READY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY
-UnflaggedApi: android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST
-UnflaggedApi: android.Manifest.permission#RECEIVE_WIFI_CREDENTIAL_CHANGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE
-UnflaggedApi: android.Manifest.permission#RECORD_BACKGROUND_AUDIO:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECORD_BACKGROUND_AUDIO
-UnflaggedApi: android.Manifest.permission#RECOVERY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECOVERY
-UnflaggedApi: android.Manifest.permission#RECOVER_KEYSTORE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RECOVER_KEYSTORE
-UnflaggedApi: android.Manifest.permission#REGISTER_CALL_PROVIDER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_CALL_PROVIDER
-UnflaggedApi: android.Manifest.permission#REGISTER_CONNECTION_MANAGER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_CONNECTION_MANAGER
 UnflaggedApi: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_NSD_OFFLOAD_ENGINE
-UnflaggedApi: android.Manifest.permission#REGISTER_SIM_SUBSCRIPTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION
-UnflaggedApi: android.Manifest.permission#REGISTER_STATS_PULL_ATOM:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_STATS_PULL_ATOM
-UnflaggedApi: android.Manifest.permission#REMOTE_DISPLAY_PROVIDER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOTE_DISPLAY_PROVIDER
-UnflaggedApi: android.Manifest.permission#REMOVE_DRM_CERTIFICATES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOVE_DRM_CERTIFICATES
-UnflaggedApi: android.Manifest.permission#REMOVE_TASKS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REMOVE_TASKS
-UnflaggedApi: android.Manifest.permission#RENOUNCE_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RENOUNCE_PERMISSIONS
 UnflaggedApi: android.Manifest.permission#REPORT_USAGE_STATS:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.REPORT_USAGE_STATS
-UnflaggedApi: android.Manifest.permission#REQUEST_NOTIFICATION_ASSISTANT_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE
-UnflaggedApi: android.Manifest.permission#RESET_PASSWORD:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESET_PASSWORD
-UnflaggedApi: android.Manifest.permission#RESTART_WIFI_SUBSYSTEM:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTART_WIFI_SUBSYSTEM
-UnflaggedApi: android.Manifest.permission#RESTORE_RUNTIME_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#RESTRICTED_VR_ACCESS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESTRICTED_VR_ACCESS
-UnflaggedApi: android.Manifest.permission#RETRIEVE_WINDOW_CONTENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RETRIEVE_WINDOW_CONTENT
-UnflaggedApi: android.Manifest.permission#REVIEW_ACCESSIBILITY_SERVICES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES
-UnflaggedApi: android.Manifest.permission#REVOKE_RUNTIME_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#ROTATE_SURFACE_FLINGER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ROTATE_SURFACE_FLINGER
-UnflaggedApi: android.Manifest.permission#SATELLITE_COMMUNICATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SATELLITE_COMMUNICATION
-UnflaggedApi: android.Manifest.permission#SCHEDULE_PRIORITIZED_ALARM:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM
-UnflaggedApi: android.Manifest.permission#SECURE_ELEMENT_PRIVILEGED_OPERATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION
-UnflaggedApi: android.Manifest.permission#SEND_CATEGORY_CAR_NOTIFICATIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_CATEGORY_CAR_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#SEND_DEVICE_CUSTOMIZATION_READY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY
-UnflaggedApi: android.Manifest.permission#SEND_SAFETY_CENTER_UPDATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
-UnflaggedApi: android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS
-UnflaggedApi: android.Manifest.permission#SEND_SMS_NO_CONFIRMATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SEND_SMS_NO_CONFIRMATION
-UnflaggedApi: android.Manifest.permission#SERIAL_PORT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SERIAL_PORT
-UnflaggedApi: android.Manifest.permission#SET_ACTIVITY_WATCHER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_ACTIVITY_WATCHER
-UnflaggedApi: android.Manifest.permission#SET_CLIP_SOURCE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_CLIP_SOURCE
-UnflaggedApi: android.Manifest.permission#SET_DEFAULT_ACCOUNT_FOR_CONTACTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS
-UnflaggedApi: android.Manifest.permission#SET_HARMFUL_APP_WARNINGS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_HARMFUL_APP_WARNINGS
-UnflaggedApi: android.Manifest.permission#SET_LOW_POWER_STANDBY_PORTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS
-UnflaggedApi: android.Manifest.permission#SET_MEDIA_KEY_LISTENER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_MEDIA_KEY_LISTENER
-UnflaggedApi: android.Manifest.permission#SET_ORIENTATION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_ORIENTATION
-UnflaggedApi: android.Manifest.permission#SET_POINTER_SPEED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_POINTER_SPEED
-UnflaggedApi: android.Manifest.permission#SET_SCREEN_COMPATIBILITY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_SCREEN_COMPATIBILITY
-UnflaggedApi: android.Manifest.permission#SET_SYSTEM_AUDIO_CAPTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
-UnflaggedApi: android.Manifest.permission#SET_UNRESTRICTED_KEEP_CLEAR_AREAS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS
-UnflaggedApi: android.Manifest.permission#SET_VOLUME_KEY_LONG_PRESS_LISTENER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER
-UnflaggedApi: android.Manifest.permission#SET_WALLPAPER_COMPONENT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_WALLPAPER_COMPONENT
-UnflaggedApi: android.Manifest.permission#SET_WALLPAPER_DIM_AMOUNT:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT
-UnflaggedApi: android.Manifest.permission#SHOW_KEYGUARD_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SHOW_KEYGUARD_MESSAGE
-UnflaggedApi: android.Manifest.permission#SHUTDOWN:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SHUTDOWN
-UnflaggedApi: android.Manifest.permission#SIGNAL_REBOOT_READINESS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SIGNAL_REBOOT_READINESS
-UnflaggedApi: android.Manifest.permission#SOUND_TRIGGER_RUN_IN_BATTERY_SAVER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER
-UnflaggedApi: android.Manifest.permission#STAGE_HEALTH_CONNECT_REMOTE_DATA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA
-UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND
-UnflaggedApi: android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES
-UnflaggedApi: android.Manifest.permission#START_REVIEW_PERMISSION_DECISIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS
-UnflaggedApi: android.Manifest.permission#START_TASKS_FROM_RECENTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_TASKS_FROM_RECENTS
-UnflaggedApi: android.Manifest.permission#STATUS_BAR_SERVICE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.STATUS_BAR_SERVICE
-UnflaggedApi: android.Manifest.permission#STOP_APP_SWITCHES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.STOP_APP_SWITCHES
-UnflaggedApi: android.Manifest.permission#SUBSTITUTE_NOTIFICATION_APP_NAME:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME
-UnflaggedApi: android.Manifest.permission#SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON
-UnflaggedApi: android.Manifest.permission#SUGGEST_EXTERNAL_TIME:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUGGEST_EXTERNAL_TIME
-UnflaggedApi: android.Manifest.permission#SUSPEND_APPS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SUSPEND_APPS
-UnflaggedApi: android.Manifest.permission#SYSTEM_APPLICATION_OVERLAY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY
-UnflaggedApi: android.Manifest.permission#SYSTEM_CAMERA:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.SYSTEM_CAMERA
-UnflaggedApi: android.Manifest.permission#TETHER_PRIVILEGED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.TETHER_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#TIS_EXTENSION_INTERFACE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.TIS_EXTENSION_INTERFACE
-UnflaggedApi: android.Manifest.permission#TOGGLE_AUTOMOTIVE_PROJECTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION
-UnflaggedApi: android.Manifest.permission#TRIGGER_LOST_MODE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.TRIGGER_LOST_MODE
-UnflaggedApi: android.Manifest.permission#TV_INPUT_HARDWARE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.TV_INPUT_HARDWARE
-UnflaggedApi: android.Manifest.permission#TV_VIRTUAL_REMOTE_CONTROLLER:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER
-UnflaggedApi: android.Manifest.permission#UNLIMITED_SHORTCUTS_API_CALLS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UNLIMITED_SHORTCUTS_API_CALLS
-UnflaggedApi: android.Manifest.permission#UPDATE_APP_OPS_STATS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_APP_OPS_STATS
-UnflaggedApi: android.Manifest.permission#UPDATE_DEVICE_MANAGEMENT_RESOURCES:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES
-UnflaggedApi: android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
-UnflaggedApi: android.Manifest.permission#UPDATE_FONTS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_FONTS
-UnflaggedApi: android.Manifest.permission#UPDATE_LOCK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPDATE_LOCK
-UnflaggedApi: android.Manifest.permission#UPGRADE_RUNTIME_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#USER_ACTIVITY:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.USER_ACTIVITY
-UnflaggedApi: android.Manifest.permission#USE_COLORIZED_NOTIFICATIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS
-UnflaggedApi: android.Manifest.permission#USE_RESERVED_DISK:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_RESERVED_DISK
-UnflaggedApi: android.Manifest.permission#UWB_PRIVILEGED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.UWB_PRIVILEGED
-UnflaggedApi: android.Manifest.permission#WHITELIST_AUTO_REVOKE_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#WHITELIST_RESTRICTED_PERMISSIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
-UnflaggedApi: android.Manifest.permission#WIFI_ACCESS_COEX_UNSAFE_CHANNELS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS
-UnflaggedApi: android.Manifest.permission#WIFI_SET_DEVICE_MOBILITY_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE
-UnflaggedApi: android.Manifest.permission#WIFI_UPDATE_COEX_UNSAFE_CHANNELS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS
-UnflaggedApi: android.Manifest.permission#WIFI_UPDATE_USABILITY_STATS_SCORE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE
-UnflaggedApi: android.Manifest.permission#WRITE_ALLOWLISTED_DEVICE_CONFIG:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#WRITE_DEVICE_CONFIG:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_DEVICE_CONFIG
-UnflaggedApi: android.Manifest.permission#WRITE_DREAM_STATE:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_DREAM_STATE
-UnflaggedApi: android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
-UnflaggedApi: android.Manifest.permission#WRITE_OBB:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_OBB
-UnflaggedApi: android.Manifest.permission#WRITE_SECURITY_LOG:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_SECURITY_LOG
-UnflaggedApi: android.Manifest.permission#WRITE_SMS:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission.WRITE_SMS
-UnflaggedApi: android.Manifest.permission_group#UNDEFINED:
-    New API must be flagged with @FlaggedApi: field android.Manifest.permission_group.UNDEFINED
-UnflaggedApi: android.R.array#config_keySystemUuidMapping:
-    New API must be flagged with @FlaggedApi: field android.R.array.config_keySystemUuidMapping
-UnflaggedApi: android.R.array#config_optionalIpSecAlgorithms:
-    New API must be flagged with @FlaggedApi: field android.R.array.config_optionalIpSecAlgorithms
-UnflaggedApi: android.R.attr#allowClearUserDataOnFailedRestore:
-    New API must be flagged with @FlaggedApi: field android.R.attr.allowClearUserDataOnFailedRestore
-UnflaggedApi: android.R.attr#gameSessionService:
-    New API must be flagged with @FlaggedApi: field android.R.attr.gameSessionService
-UnflaggedApi: android.R.attr#hotwordDetectionService:
-    New API must be flagged with @FlaggedApi: field android.R.attr.hotwordDetectionService
-UnflaggedApi: android.R.attr#isVrOnly:
-    New API must be flagged with @FlaggedApi: field android.R.attr.isVrOnly
-UnflaggedApi: android.R.attr#minExtensionVersion:
-    New API must be flagged with @FlaggedApi: field android.R.attr.minExtensionVersion
-UnflaggedApi: android.R.attr#playHomeTransitionSound:
-    New API must be flagged with @FlaggedApi: field android.R.attr.playHomeTransitionSound
-UnflaggedApi: android.R.attr#requiredSystemPropertyName:
-    New API must be flagged with @FlaggedApi: field android.R.attr.requiredSystemPropertyName
-UnflaggedApi: android.R.attr#requiredSystemPropertyValue:
-    New API must be flagged with @FlaggedApi: field android.R.attr.requiredSystemPropertyValue
-UnflaggedApi: android.R.attr#sdkVersion:
-    New API must be flagged with @FlaggedApi: field android.R.attr.sdkVersion
-UnflaggedApi: android.R.attr#supportsAmbientMode:
-    New API must be flagged with @FlaggedApi: field android.R.attr.supportsAmbientMode
-UnflaggedApi: android.R.attr#userRestriction:
-    New API must be flagged with @FlaggedApi: field android.R.attr.userRestriction
-UnflaggedApi: android.R.attr#visualQueryDetectionService:
-    New API must be flagged with @FlaggedApi: field android.R.attr.visualQueryDetectionService
-UnflaggedApi: android.R.bool#config_enableDefaultNotes:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_enableDefaultNotes
-UnflaggedApi: android.R.bool#config_enableDefaultNotesForWorkProfile:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_enableDefaultNotesForWorkProfile
-UnflaggedApi: android.R.bool#config_enableQrCodeScannerOnLockScreen:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_enableQrCodeScannerOnLockScreen
-UnflaggedApi: android.R.bool#config_safetyProtectionEnabled:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_safetyProtectionEnabled
-UnflaggedApi: android.R.bool#config_sendPackageName:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_sendPackageName
-UnflaggedApi: android.R.bool#config_showDefaultAssistant:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultAssistant
-UnflaggedApi: android.R.bool#config_showDefaultEmergency:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultEmergency
-UnflaggedApi: android.R.bool#config_showDefaultHome:
-    New API must be flagged with @FlaggedApi: field android.R.bool.config_showDefaultHome
-UnflaggedApi: android.R.color#system_notification_accent_color:
-    New API must be flagged with @FlaggedApi: field android.R.color.system_notification_accent_color
-UnflaggedApi: android.R.dimen#config_restrictedIconSize:
-    New API must be flagged with @FlaggedApi: field android.R.dimen.config_restrictedIconSize
-UnflaggedApi: android.R.dimen#config_viewConfigurationHandwritingGestureLineMargin:
-    New API must be flagged with @FlaggedApi: field android.R.dimen.config_viewConfigurationHandwritingGestureLineMargin
-UnflaggedApi: android.R.drawable#ic_info:
-    New API must be flagged with @FlaggedApi: field android.R.drawable.ic_info
-UnflaggedApi: android.R.drawable#ic_safety_protection:
-    New API must be flagged with @FlaggedApi: field android.R.drawable.ic_safety_protection
-UnflaggedApi: android.R.raw#loaderror:
-    New API must be flagged with @FlaggedApi: field android.R.raw.loaderror
-UnflaggedApi: android.R.raw#nodomain:
-    New API must be flagged with @FlaggedApi: field android.R.raw.nodomain
-UnflaggedApi: android.R.string#config_customMediaKeyDispatcher:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_customMediaKeyDispatcher
-UnflaggedApi: android.R.string#config_customMediaSessionPolicyProvider:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_customMediaSessionPolicyProvider
-UnflaggedApi: android.R.string#config_defaultAssistant:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultAssistant
-UnflaggedApi: android.R.string#config_defaultAutomotiveNavigation:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultAutomotiveNavigation
-UnflaggedApi: android.R.string#config_defaultBrowser:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultBrowser
-UnflaggedApi: android.R.string#config_defaultCallRedirection:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultCallRedirection
-UnflaggedApi: android.R.string#config_defaultCallScreening:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultCallScreening
-UnflaggedApi: android.R.string#config_defaultDialer:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultDialer
-UnflaggedApi: android.R.string#config_defaultNotes:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultNotes
 UnflaggedApi: android.R.string#config_defaultRetailDemo:
     New API must be flagged with @FlaggedApi: field android.R.string.config_defaultRetailDemo
-UnflaggedApi: android.R.string#config_defaultSms:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_defaultSms
-UnflaggedApi: android.R.string#config_devicePolicyManagement:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_devicePolicyManagement
-UnflaggedApi: android.R.string#config_feedbackIntentExtraKey:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_feedbackIntentExtraKey
-UnflaggedApi: android.R.string#config_feedbackIntentNameKey:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_feedbackIntentNameKey
-UnflaggedApi: android.R.string#config_helpIntentExtraKey:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_helpIntentExtraKey
-UnflaggedApi: android.R.string#config_helpIntentNameKey:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_helpIntentNameKey
-UnflaggedApi: android.R.string#config_helpPackageNameKey:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_helpPackageNameKey
-UnflaggedApi: android.R.string#config_helpPackageNameValue:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_helpPackageNameValue
-UnflaggedApi: android.R.string#config_systemActivityRecognizer:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemActivityRecognizer
-UnflaggedApi: android.R.string#config_systemAmbientAudioIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemAmbientAudioIntelligence
-UnflaggedApi: android.R.string#config_systemAppProtectionService:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemAppProtectionService
-UnflaggedApi: android.R.string#config_systemAudioIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemAudioIntelligence
-UnflaggedApi: android.R.string#config_systemAutomotiveCalendarSyncManager:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveCalendarSyncManager
-UnflaggedApi: android.R.string#config_systemAutomotiveCluster:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveCluster
-UnflaggedApi: android.R.string#config_systemAutomotiveProjection:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemAutomotiveProjection
-UnflaggedApi: android.R.string#config_systemCallStreaming:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemCallStreaming
-UnflaggedApi: android.R.string#config_systemCompanionDeviceProvider:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemCompanionDeviceProvider
-UnflaggedApi: android.R.string#config_systemContacts:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemContacts
-UnflaggedApi: android.R.string#config_systemFinancedDeviceController:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemFinancedDeviceController
-UnflaggedApi: android.R.string#config_systemGallery:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemGallery
-UnflaggedApi: android.R.string#config_systemNotificationIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemNotificationIntelligence
-UnflaggedApi: android.R.string#config_systemSettingsIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemSettingsIntelligence
-UnflaggedApi: android.R.string#config_systemShell:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemShell
-UnflaggedApi: android.R.string#config_systemSpeechRecognizer:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemSpeechRecognizer
-UnflaggedApi: android.R.string#config_systemSupervision:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemSupervision
-UnflaggedApi: android.R.string#config_systemTelevisionNotificationHandler:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemTelevisionNotificationHandler
-UnflaggedApi: android.R.string#config_systemTextIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemTextIntelligence
-UnflaggedApi: android.R.string#config_systemUi:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemUi
-UnflaggedApi: android.R.string#config_systemUiIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemUiIntelligence
-UnflaggedApi: android.R.string#config_systemVisualIntelligence:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemVisualIntelligence
-UnflaggedApi: android.R.string#config_systemWearHealthService:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemWearHealthService
-UnflaggedApi: android.R.string#config_systemWellbeing:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemWellbeing
-UnflaggedApi: android.R.string#config_systemWifiCoexManager:
-    New API must be flagged with @FlaggedApi: field android.R.string.config_systemWifiCoexManager
-UnflaggedApi: android.R.string#safety_protection_display_text:
-    New API must be flagged with @FlaggedApi: field android.R.string.safety_protection_display_text
-UnflaggedApi: android.R.style#Theme_DeviceDefault_DocumentsUI:
-    New API must be flagged with @FlaggedApi: field android.R.style.Theme_DeviceDefault_DocumentsUI
-UnflaggedApi: android.R.style#Theme_Leanback_FormWizard:
-    New API must be flagged with @FlaggedApi: field android.R.style.Theme_Leanback_FormWizard
 UnflaggedApi: android.app.ActivityManager#getExternalHistoricalProcessStartReasons(String, int):
     New API must be flagged with @FlaggedApi: method android.app.ActivityManager.getExternalHistoricalProcessStartReasons(String,int)
 UnflaggedApi: android.app.AppOpsManager#OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO:
     New API must be flagged with @FlaggedApi: field android.app.AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO
-UnflaggedApi: android.app.AppOpsManager.AttributedHistoricalOps#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.AttributedHistoricalOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.AttributedHistoricalOps#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.AttributedHistoricalOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOp#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOp.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalOp#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOp.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalOps#toString():
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalOps.toString()
-UnflaggedApi: android.app.AppOpsManager.HistoricalPackageOps#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalPackageOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalPackageOps#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalPackageOps.hashCode()
-UnflaggedApi: android.app.AppOpsManager.HistoricalUidOps#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalUidOps.equals(Object)
-UnflaggedApi: android.app.AppOpsManager.HistoricalUidOps#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.AppOpsManager.HistoricalUidOps.hashCode()
-UnflaggedApi: android.app.GameModeConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.GameModeConfiguration.equals(Object)
-UnflaggedApi: android.app.GameModeConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.GameModeConfiguration.hashCode()
-UnflaggedApi: android.app.StatusBarManager.DisableInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.app.StatusBarManager.DisableInfo.toString()
-UnflaggedApi: android.app.Vr2dDisplayProperties#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.equals(Object)
-UnflaggedApi: android.app.Vr2dDisplayProperties#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.hashCode()
-UnflaggedApi: android.app.Vr2dDisplayProperties#toString():
-    New API must be flagged with @FlaggedApi: method android.app.Vr2dDisplayProperties.toString()
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.hashCode()
-UnflaggedApi: android.app.admin.AccountTypePolicyKey#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.AccountTypePolicyKey.toString()
-UnflaggedApi: android.app.admin.Authority#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.Authority.equals(Object)
-UnflaggedApi: android.app.admin.Authority#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.Authority.hashCode()
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.equals(Object)
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.hashCode()
-UnflaggedApi: android.app.admin.DeviceAdminAuthority#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DeviceAdminAuthority.toString()
-UnflaggedApi: android.app.admin.DevicePolicyDrawableResource#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyDrawableResource.equals(Object)
-UnflaggedApi: android.app.admin.DevicePolicyDrawableResource#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyDrawableResource.hashCode()
-UnflaggedApi: android.app.admin.DevicePolicyKeyguardService#onDestroy():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyKeyguardService.onDestroy()
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings:
-    New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings:
-    New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings#HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings#WORK_PROFILE_DEFAULT_APPS_TITLE:
-    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings:
-    New API must be flagged with @FlaggedApi: class android.app.admin.DevicePolicyResources.Strings.PermissionSettings
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyResources.Strings.PermissionSettings#LOCATION_AUTO_GRANTED_MESSAGE:
-    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyResources.Strings.PermissionSettings.LOCATION_AUTO_GRANTED_MESSAGE
-UnflaggedApi: android.app.admin.DevicePolicyState#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyState.toString()
-UnflaggedApi: android.app.admin.DevicePolicyStringResource#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyStringResource.equals(Object)
-UnflaggedApi: android.app.admin.DevicePolicyStringResource#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DevicePolicyStringResource.hashCode()
-UnflaggedApi: android.app.admin.DpcAuthority#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.equals(Object)
-UnflaggedApi: android.app.admin.DpcAuthority#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.hashCode()
-UnflaggedApi: android.app.admin.DpcAuthority#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.DpcAuthority.toString()
-UnflaggedApi: android.app.admin.EnforcingAdmin#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.equals(Object)
-UnflaggedApi: android.app.admin.EnforcingAdmin#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.hashCode()
-UnflaggedApi: android.app.admin.EnforcingAdmin#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.EnforcingAdmin.toString()
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.hashCode()
-UnflaggedApi: android.app.admin.IntentFilterPolicyKey#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.IntentFilterPolicyKey.toString()
-UnflaggedApi: android.app.admin.LockTaskPolicy#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.equals(Object)
-UnflaggedApi: android.app.admin.LockTaskPolicy#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.hashCode()
-UnflaggedApi: android.app.admin.LockTaskPolicy#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.LockTaskPolicy.toString()
-UnflaggedApi: android.app.admin.NoArgsPolicyKey#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.NoArgsPolicyKey.toString()
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PackagePermissionPolicyKey#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.PackagePermissionPolicyKey.toString()
-UnflaggedApi: android.app.admin.PackagePolicyKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PackagePolicyKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PackagePolicyKey#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.PackagePolicyKey.toString()
-UnflaggedApi: android.app.admin.PolicyKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.PolicyKey.equals(Object)
-UnflaggedApi: android.app.admin.PolicyKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.PolicyKey.hashCode()
-UnflaggedApi: android.app.admin.PolicyState#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.PolicyState.toString()
-UnflaggedApi: android.app.admin.RoleAuthority#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.equals(Object)
-UnflaggedApi: android.app.admin.RoleAuthority#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.hashCode()
-UnflaggedApi: android.app.admin.RoleAuthority#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.RoleAuthority.toString()
-UnflaggedApi: android.app.admin.UnknownAuthority#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.equals(Object)
-UnflaggedApi: android.app.admin.UnknownAuthority#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.hashCode()
-UnflaggedApi: android.app.admin.UnknownAuthority#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.UnknownAuthority.toString()
-UnflaggedApi: android.app.admin.UserRestrictionPolicyKey#toString():
-    New API must be flagged with @FlaggedApi: method android.app.admin.UserRestrictionPolicyKey.toString()
-UnflaggedApi: android.app.ambientcontext.AmbientContextEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.app.ambientcontext.AmbientContextEvent.toString()
-UnflaggedApi: android.app.ambientcontext.AmbientContextEventRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.app.ambientcontext.AmbientContextEventRequest.toString()
-UnflaggedApi: android.app.assist.ActivityId#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.equals(Object)
-UnflaggedApi: android.app.assist.ActivityId#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.hashCode()
-UnflaggedApi: android.app.assist.ActivityId#toString():
-    New API must be flagged with @FlaggedApi: method android.app.assist.ActivityId.toString()
-UnflaggedApi: android.app.assist.AssistStructure.ViewNode#ViewNode():
-    New API must be flagged with @FlaggedApi: constructor android.app.assist.AssistStructure.ViewNode()
-UnflaggedApi: android.app.backup.RestoreDescription#toString():
-    New API must be flagged with @FlaggedApi: method android.app.backup.RestoreDescription.toString()
-UnflaggedApi: android.app.cloudsearch.SearchRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.hashCode()
-UnflaggedApi: android.app.cloudsearch.SearchRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchRequest.toString()
-UnflaggedApi: android.app.cloudsearch.SearchResponse#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResponse.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchResponse#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResponse.hashCode()
-UnflaggedApi: android.app.cloudsearch.SearchResult#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResult.equals(Object)
-UnflaggedApi: android.app.cloudsearch.SearchResult#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.cloudsearch.SearchResult.hashCode()
-UnflaggedApi: android.app.prediction.AppPredictionContext#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionContext.equals(Object)
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.equals(Object)
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.hashCode()
-UnflaggedApi: android.app.prediction.AppPredictionSessionId#toString():
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictionSessionId.toString()
-UnflaggedApi: android.app.prediction.AppPredictor#finalize():
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppPredictor.finalize()
-UnflaggedApi: android.app.prediction.AppTarget#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppTarget.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetEvent.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetId#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetId.equals(Object)
-UnflaggedApi: android.app.prediction.AppTargetId#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.prediction.AppTargetId.hashCode()
-UnflaggedApi: android.app.search.SearchAction#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.equals(Object)
-UnflaggedApi: android.app.search.SearchAction#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.hashCode()
-UnflaggedApi: android.app.search.SearchAction#toString():
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchAction.toString()
-UnflaggedApi: android.app.search.SearchSessionId#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.equals(Object)
-UnflaggedApi: android.app.search.SearchSessionId#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.hashCode()
-UnflaggedApi: android.app.search.SearchSessionId#toString():
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchSessionId.toString()
-UnflaggedApi: android.app.search.SearchTargetEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchTargetEvent.equals(Object)
-UnflaggedApi: android.app.search.SearchTargetEvent#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.search.SearchTargetEvent.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceAction#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceAction#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceAction#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceAction.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceConfig.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceConfig.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceSessionId#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceSessionId.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.equals(Object)
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.hashCode()
-UnflaggedApi: android.app.smartspace.SmartspaceTarget#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTarget.toString()
-UnflaggedApi: android.app.smartspace.SmartspaceTargetEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.SmartspaceTargetEvent.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.CombinedCardsTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.CombinedCardsTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.HeadToHeadTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.HeadToHeadTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Icon#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Icon.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubCardTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubCardTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubImageTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubImageTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.SubListTemplateData#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.SubListTemplateData.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.TapAction#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.TapAction.toString()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.equals(Object)
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.hashCode()
-UnflaggedApi: android.app.smartspace.uitemplatedata.Text#toString():
-    New API must be flagged with @FlaggedApi: method android.app.smartspace.uitemplatedata.Text.toString()
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.equals(Object)
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.hashCode()
-UnflaggedApi: android.app.time.ExternalTimeSuggestion#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.ExternalTimeSuggestion.toString()
-UnflaggedApi: android.app.time.TimeCapabilities#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.equals(Object)
-UnflaggedApi: android.app.time.TimeCapabilities#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.hashCode()
-UnflaggedApi: android.app.time.TimeCapabilities#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilities.toString()
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.equals(Object)
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.hashCode()
-UnflaggedApi: android.app.time.TimeCapabilitiesAndConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeCapabilitiesAndConfig.toString()
-UnflaggedApi: android.app.time.TimeConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.equals(Object)
-UnflaggedApi: android.app.time.TimeConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.hashCode()
-UnflaggedApi: android.app.time.TimeConfiguration#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeConfiguration.toString()
-UnflaggedApi: android.app.time.TimeState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeState.equals(Object)
-UnflaggedApi: android.app.time.TimeState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeState.hashCode()
-UnflaggedApi: android.app.time.TimeState#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeState.toString()
-UnflaggedApi: android.app.time.TimeZoneCapabilities#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneCapabilities#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.hashCode()
-UnflaggedApi: android.app.time.TimeZoneCapabilities#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilities.toString()
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.hashCode()
-UnflaggedApi: android.app.time.TimeZoneCapabilitiesAndConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneCapabilitiesAndConfig.toString()
-UnflaggedApi: android.app.time.TimeZoneConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.hashCode()
-UnflaggedApi: android.app.time.TimeZoneConfiguration#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneConfiguration.toString()
-UnflaggedApi: android.app.time.TimeZoneState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.equals(Object)
-UnflaggedApi: android.app.time.TimeZoneState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.hashCode()
-UnflaggedApi: android.app.time.TimeZoneState#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.TimeZoneState.toString()
-UnflaggedApi: android.app.time.UnixEpochTime#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.equals(Object)
-UnflaggedApi: android.app.time.UnixEpochTime#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.hashCode()
-UnflaggedApi: android.app.time.UnixEpochTime#toString():
-    New API must be flagged with @FlaggedApi: method android.app.time.UnixEpochTime.toString()
-UnflaggedApi: android.app.usage.BroadcastResponseStats#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.equals(Object)
-UnflaggedApi: android.app.usage.BroadcastResponseStats#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.hashCode()
-UnflaggedApi: android.app.usage.BroadcastResponseStats#toString():
-    New API must be flagged with @FlaggedApi: method android.app.usage.BroadcastResponseStats.toString()
-UnflaggedApi: android.app.usage.CacheQuotaHint#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaHint.equals(Object)
-UnflaggedApi: android.app.usage.CacheQuotaHint#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaHint.hashCode()
-UnflaggedApi: android.app.usage.CacheQuotaService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.app.usage.CacheQuotaService.onCreate()
-UnflaggedApi: android.app.usage.UsageEvents.Event#NOTIFICATION_INTERRUPTION:
-    New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION
-UnflaggedApi: android.app.usage.UsageEvents.Event#NOTIFICATION_SEEN:
-    New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN
-UnflaggedApi: android.app.usage.UsageEvents.Event#SLICE_PINNED:
-    New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SLICE_PINNED
-UnflaggedApi: android.app.usage.UsageEvents.Event#SLICE_PINNED_PRIV:
-    New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV
-UnflaggedApi: android.app.usage.UsageEvents.Event#SYSTEM_INTERACTION:
-    New API must be flagged with @FlaggedApi: field android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION
-UnflaggedApi: android.app.usage.UsageEvents.Event#getInstanceId():
-    New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getInstanceId()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getNotificationChannelId():
-    New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getNotificationChannelId()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getTaskRootClassName():
-    New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getTaskRootClassName()
-UnflaggedApi: android.app.usage.UsageEvents.Event#getTaskRootPackageName():
-    New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.getTaskRootPackageName()
-UnflaggedApi: android.app.usage.UsageEvents.Event#isInstantApp():
-    New API must be flagged with @FlaggedApi: method android.app.usage.UsageEvents.Event.isInstantApp()
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectRequest.equals(Object)
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectRequest.hashCode()
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectResponse#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectResponse.equals(Object)
-UnflaggedApi: android.app.wallpapereffectsgeneration.CinematicEffectResponse#hashCode():
-    New API must be flagged with @FlaggedApi: method android.app.wallpapereffectsgeneration.CinematicEffectResponse.hashCode()
 UnflaggedApi: android.companion.virtual.VirtualDeviceManager.VirtualDevice#getPersistentDeviceId():
     New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceManager.VirtualDevice.getPersistentDeviceId()
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.equals(Object)
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#hashCode():
-    New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.hashCode()
-UnflaggedApi: android.companion.virtual.VirtualDeviceParams#toString():
-    New API must be flagged with @FlaggedApi: method android.companion.virtual.VirtualDeviceParams.toString()
-UnflaggedApi: android.companion.virtual.sensor.VirtualSensorConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.companion.virtual.sensor.VirtualSensorConfig.toString()
+UnflaggedApi: android.content.Context#THREAD_NETWORK_SERVICE:
+    New API must be flagged with @FlaggedApi: field android.content.Context.THREAD_NETWORK_SERVICE
 UnflaggedApi: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
     New API must be flagged with @FlaggedApi: field android.content.Intent.ACTION_UNARCHIVE_PACKAGE
-UnflaggedApi: android.content.integrity.Rule#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.equals(Object)
-UnflaggedApi: android.content.integrity.Rule#hashCode():
-    New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.hashCode()
-UnflaggedApi: android.content.integrity.Rule#toString():
-    New API must be flagged with @FlaggedApi: method android.content.integrity.Rule.toString()
-UnflaggedApi: android.content.pm.PackageArchiver:
-    New API must be flagged with @FlaggedApi: class android.content.pm.PackageArchiver
-UnflaggedApi: android.content.pm.PackageArchiver#EXTRA_UNARCHIVE_ALL_USERS:
-    New API must be flagged with @FlaggedApi: field android.content.pm.PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS
-UnflaggedApi: android.content.pm.PackageArchiver#EXTRA_UNARCHIVE_PACKAGE_NAME:
-    New API must be flagged with @FlaggedApi: field android.content.pm.PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME
-UnflaggedApi: android.content.pm.PackageArchiver#requestArchive(String, android.content.IntentSender):
-    New API must be flagged with @FlaggedApi: method android.content.pm.PackageArchiver.requestArchive(String,android.content.IntentSender)
-UnflaggedApi: android.content.pm.PackageArchiver#requestUnarchive(String):
-    New API must be flagged with @FlaggedApi: method android.content.pm.PackageArchiver.requestUnarchive(String)
-UnflaggedApi: android.content.pm.PackageInfo#isArchived:
-    New API must be flagged with @FlaggedApi: field android.content.pm.PackageInfo.isArchived
 UnflaggedApi: android.content.pm.PackageInstaller#readInstallInfo(android.os.ParcelFileDescriptor, String, int):
     New API must be flagged with @FlaggedApi: method android.content.pm.PackageInstaller.readInstallInfo(android.os.ParcelFileDescriptor,String,int)
 UnflaggedApi: android.content.pm.PackageInstaller.InstallInfo#calculateInstalledSize(android.content.pm.PackageInstaller.SessionParams, android.os.ParcelFileDescriptor):
@@ -1579,320 +233,6 @@
     New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID
 UnflaggedApi: android.content.pm.PackageManager#MATCH_ARCHIVED_PACKAGES:
     New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES
-UnflaggedApi: android.content.pm.PackageManager#getPackageArchiver():
-    New API must be flagged with @FlaggedApi: method android.content.pm.PackageManager.getPackageArchiver()
-UnflaggedApi: android.content.pm.SuspendDialogInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.equals(Object)
-UnflaggedApi: android.content.pm.SuspendDialogInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.hashCode()
-UnflaggedApi: android.content.pm.SuspendDialogInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.content.pm.SuspendDialogInfo.toString()
-UnflaggedApi: android.content.pm.UserProperties#toString():
-    New API must be flagged with @FlaggedApi: method android.content.pm.UserProperties.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#hashCode():
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.hashCode()
-UnflaggedApi: android.content.pm.verify.domain.DomainOwner#toString():
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainOwner.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.hashCode()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationInfo.toString()
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationRequest.equals(Object)
-UnflaggedApi: android.content.pm.verify.domain.DomainVerificationRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.content.pm.verify.domain.DomainVerificationRequest.hashCode()
-UnflaggedApi: android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_CONVENIENCE:
-    New API must be flagged with @FlaggedApi: field android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE
-UnflaggedApi: android.hardware.biometrics.BiometricManager.Authenticators#EMPTY_SET:
-    New API must be flagged with @FlaggedApi: field android.hardware.biometrics.BiometricManager.Authenticators.EMPTY_SET
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.equals(Object)
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.hashCode()
-UnflaggedApi: android.hardware.display.AmbientBrightnessDayStats#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientBrightnessDayStats.toString()
-UnflaggedApi: android.hardware.display.BrightnessChangeEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessChangeEvent.toString()
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.equals(Object)
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.hashCode()
-UnflaggedApi: android.hardware.display.BrightnessConfiguration#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessConfiguration.toString()
-UnflaggedApi: android.hardware.display.BrightnessCorrection#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.equals(Object)
-UnflaggedApi: android.hardware.display.BrightnessCorrection#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.hashCode()
-UnflaggedApi: android.hardware.display.BrightnessCorrection#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.display.BrightnessCorrection.toString()
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.equals(Object)
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.hashCode()
-UnflaggedApi: android.hardware.hdmi.HdmiDeviceInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiDeviceInfo.toString()
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.equals(Object)
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.hashCode()
-UnflaggedApi: android.hardware.hdmi.HdmiPortInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.hdmi.HdmiPortInfo.toString()
-UnflaggedApi: android.hardware.location.ContextHubClient#finalize():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubClient.finalize()
-UnflaggedApi: android.hardware.location.ContextHubInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubInfo.equals(Object)
-UnflaggedApi: android.hardware.location.ContextHubInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubInfo.toString()
-UnflaggedApi: android.hardware.location.ContextHubIntentEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubIntentEvent.equals(Object)
-UnflaggedApi: android.hardware.location.ContextHubIntentEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubIntentEvent.toString()
-UnflaggedApi: android.hardware.location.ContextHubMessage#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.ContextHubMessage.toString()
-UnflaggedApi: android.hardware.location.GeofenceHardwareMonitorEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.GeofenceHardwareMonitorEvent.toString()
-UnflaggedApi: android.hardware.location.MemoryRegion#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.location.MemoryRegion.equals(Object)
-UnflaggedApi: android.hardware.location.MemoryRegion#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.MemoryRegion.toString()
-UnflaggedApi: android.hardware.location.NanoApp#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoApp.toString()
-UnflaggedApi: android.hardware.location.NanoAppFilter#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppFilter.toString()
-UnflaggedApi: android.hardware.location.NanoAppInstanceInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppInstanceInfo.toString()
-UnflaggedApi: android.hardware.location.NanoAppMessage#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppMessage.equals(Object)
-UnflaggedApi: android.hardware.location.NanoAppMessage#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppMessage.toString()
-UnflaggedApi: android.hardware.location.NanoAppRpcService#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.equals(Object)
-UnflaggedApi: android.hardware.location.NanoAppRpcService#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.hashCode()
-UnflaggedApi: android.hardware.location.NanoAppRpcService#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.location.NanoAppRpcService.toString()
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramList.Filter#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramList.Filter.toString()
-UnflaggedApi: android.hardware.radio.ProgramSelector#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramSelector#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramSelector#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.toString()
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.equals(Object)
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.hashCode()
-UnflaggedApi: android.hardware.radio.ProgramSelector.Identifier#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.ProgramSelector.Identifier.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.AmBandDescriptor#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.AmBandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.BandConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.BandDescriptor#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.BandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandConfig.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.FmBandDescriptor#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.FmBandDescriptor.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.ModuleProperties#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ModuleProperties.toString()
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.hashCode()
-UnflaggedApi: android.hardware.radio.RadioManager.ProgramInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioManager.ProgramInfo.toString()
-UnflaggedApi: android.hardware.radio.RadioMetadata#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.equals(Object)
-UnflaggedApi: android.hardware.radio.RadioMetadata#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.hashCode()
-UnflaggedApi: android.hardware.radio.RadioMetadata#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.radio.RadioMetadata.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.Keyphrase#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.Keyphrase.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModelParamRange#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModelParamRange.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModelParamRange#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModelParamRange.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.ModuleProperties.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.hashCode()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.RecognitionEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.RecognitionEvent.toString()
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.SoundModel#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.SoundModel.equals(Object)
-UnflaggedApi: android.hardware.soundtrigger.SoundTrigger.SoundModel#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.soundtrigger.SoundTrigger.SoundModel.hashCode()
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.equals(Object)
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.hashCode()
-UnflaggedApi: android.hardware.usb.DisplayPortAltModeInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.usb.DisplayPortAltModeInfo.toString()
-UnflaggedApi: android.hardware.usb.UsbPort#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.usb.UsbPort.toString()
-UnflaggedApi: android.hardware.usb.UsbPortStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.hardware.usb.UsbPortStatus.toString()
-UnflaggedApi: android.location.CorrelationVector#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.equals(Object)
-UnflaggedApi: android.location.CorrelationVector#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.hashCode()
-UnflaggedApi: android.location.CorrelationVector#toString():
-    New API must be flagged with @FlaggedApi: method android.location.CorrelationVector.toString()
-UnflaggedApi: android.location.Country#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.Country.equals(Object)
-UnflaggedApi: android.location.Country#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.Country.hashCode()
-UnflaggedApi: android.location.Country#toString():
-    New API must be flagged with @FlaggedApi: method android.location.Country.toString()
-UnflaggedApi: android.location.GnssExcessPathInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.equals(Object)
-UnflaggedApi: android.location.GnssExcessPathInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.hashCode()
-UnflaggedApi: android.location.GnssExcessPathInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GnssExcessPathInfo.toString()
-UnflaggedApi: android.location.GnssMeasurementCorrections#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementCorrections.toString()
-UnflaggedApi: android.location.GnssMeasurementRequest#getWorkSource():
-    New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.getWorkSource()
-UnflaggedApi: android.location.GnssMeasurementRequest.Builder#setWorkSource(android.os.WorkSource):
-    New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.Builder.setWorkSource(android.os.WorkSource)
-UnflaggedApi: android.location.GnssReflectingPlane#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.equals(Object)
-UnflaggedApi: android.location.GnssReflectingPlane#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.hashCode()
-UnflaggedApi: android.location.GnssReflectingPlane#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GnssReflectingPlane.toString()
-UnflaggedApi: android.location.GnssRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.GnssRequest.equals(Object)
-UnflaggedApi: android.location.GnssRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.GnssRequest.hashCode()
-UnflaggedApi: android.location.GnssRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GnssRequest.toString()
-UnflaggedApi: android.location.GnssSingleSatCorrection#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.equals(Object)
-UnflaggedApi: android.location.GnssSingleSatCorrection#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.hashCode()
-UnflaggedApi: android.location.GnssSingleSatCorrection#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GnssSingleSatCorrection.toString()
-UnflaggedApi: android.location.GpsClock#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GpsClock.toString()
-UnflaggedApi: android.location.GpsMeasurement#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GpsMeasurement.toString()
-UnflaggedApi: android.location.GpsMeasurementsEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GpsMeasurementsEvent.toString()
-UnflaggedApi: android.location.GpsNavigationMessage#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GpsNavigationMessage.toString()
-UnflaggedApi: android.location.GpsNavigationMessageEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.location.GpsNavigationMessageEvent.toString()
-UnflaggedApi: android.location.LastLocationRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.equals(Object)
-UnflaggedApi: android.location.LastLocationRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.hashCode()
-UnflaggedApi: android.location.LastLocationRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.location.LastLocationRequest.toString()
-UnflaggedApi: android.location.SatellitePvt#toString():
-    New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.toString()
-UnflaggedApi: android.location.SatellitePvt.ClockInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.ClockInfo.toString()
-UnflaggedApi: android.location.SatellitePvt.PositionEcef#toString():
-    New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.PositionEcef.toString()
-UnflaggedApi: android.location.SatellitePvt.VelocityEcef#toString():
-    New API must be flagged with @FlaggedApi: method android.location.SatellitePvt.VelocityEcef.toString()
-UnflaggedApi: android.location.provider.ProviderRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.equals(Object)
-UnflaggedApi: android.location.provider.ProviderRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.hashCode()
-UnflaggedApi: android.location.provider.ProviderRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.location.provider.ProviderRequest.toString()
-UnflaggedApi: android.media.AudioDeviceAttributes#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.equals(Object)
-UnflaggedApi: android.media.AudioDeviceAttributes#hashCode():
-    New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.hashCode()
-UnflaggedApi: android.media.AudioDeviceAttributes#toString():
-    New API must be flagged with @FlaggedApi: method android.media.AudioDeviceAttributes.toString()
-UnflaggedApi: android.media.AudioFocusInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.AudioFocusInfo.equals(Object)
-UnflaggedApi: android.media.AudioFocusInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.media.AudioFocusInfo.hashCode()
-UnflaggedApi: android.media.MediaRecorder.AudioSource#ECHO_REFERENCE:
-    New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.ECHO_REFERENCE
-UnflaggedApi: android.media.MediaRecorder.AudioSource#HOTWORD:
-    New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.HOTWORD
-UnflaggedApi: android.media.MediaRecorder.AudioSource#RADIO_TUNER:
-    New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.RADIO_TUNER
-UnflaggedApi: android.media.MediaRecorder.AudioSource#ULTRASOUND:
-    New API must be flagged with @FlaggedApi: field android.media.MediaRecorder.AudioSource.ULTRASOUND
-UnflaggedApi: android.media.NearbyDevice#toString():
-    New API must be flagged with @FlaggedApi: method android.media.NearbyDevice.toString()
-UnflaggedApi: android.media.VolumeInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.equals(Object)
-UnflaggedApi: android.media.VolumeInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.hashCode()
-UnflaggedApi: android.media.VolumeInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.media.VolumeInfo.toString()
 UnflaggedApi: android.media.audiopolicy.AudioMix#CREATOR:
     New API must be flagged with @FlaggedApi: field android.media.audiopolicy.AudioMix.CREATOR
 UnflaggedApi: android.media.audiopolicy.AudioMix#describeContents():
@@ -1903,578 +243,22 @@
     New API must be flagged with @FlaggedApi: field android.media.audiopolicy.AudioMixingRule.CREATOR
 UnflaggedApi: android.media.audiopolicy.AudioMixingRule#describeContents():
     New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.describeContents()
-UnflaggedApi: android.media.audiopolicy.AudioMixingRule#hashCode():
-    New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.hashCode()
 UnflaggedApi: android.media.audiopolicy.AudioMixingRule#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.media.audiopolicy.AudioPolicy#updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>):
     New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioPolicy.updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>)
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.equals(Object)
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#hashCode():
-    New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.hashCode()
-UnflaggedApi: android.media.audiopolicy.AudioProductStrategy#toString():
-    New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioProductStrategy.toString()
-UnflaggedApi: android.media.audiopolicy.AudioVolumeGroup#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioVolumeGroup.equals(Object)
-UnflaggedApi: android.media.audiopolicy.AudioVolumeGroup#toString():
-    New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioVolumeGroup.toString()
-UnflaggedApi: android.media.musicrecognition.MusicRecognitionService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.media.musicrecognition.MusicRecognitionService.onCreate()
-UnflaggedApi: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent):
-    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerDetectionService.onUnbind(android.content.Intent)
-UnflaggedApi: android.media.tv.TunedInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.equals(Object)
-UnflaggedApi: android.media.tv.TunedInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.hashCode()
-UnflaggedApi: android.media.tv.TunedInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.media.tv.TunedInfo.toString()
-UnflaggedApi: android.media.tv.TvInputHardwareInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.media.tv.TvInputHardwareInfo.toString()
-UnflaggedApi: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle):
-    New API must be flagged with @FlaggedApi: method android.media.tv.TvRecordingClient.RecordingCallback.onEvent(String,String,android.os.Bundle)
-UnflaggedApi: android.media.tv.TvStreamConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.media.tv.TvStreamConfig.equals(Object)
-UnflaggedApi: android.media.tv.TvStreamConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.media.tv.TvStreamConfig.toString()
-UnflaggedApi: android.net.MatchAllNetworkSpecifier#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.MatchAllNetworkSpecifier.equals(Object)
-UnflaggedApi: android.net.MatchAllNetworkSpecifier#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.MatchAllNetworkSpecifier.hashCode()
-UnflaggedApi: android.net.NetworkKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.NetworkKey.equals(Object)
-UnflaggedApi: android.net.NetworkKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.NetworkKey.hashCode()
-UnflaggedApi: android.net.NetworkKey#toString():
-    New API must be flagged with @FlaggedApi: method android.net.NetworkKey.toString()
-UnflaggedApi: android.net.RssiCurve#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.RssiCurve.equals(Object)
-UnflaggedApi: android.net.RssiCurve#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.RssiCurve.hashCode()
-UnflaggedApi: android.net.RssiCurve#toString():
-    New API must be flagged with @FlaggedApi: method android.net.RssiCurve.toString()
-UnflaggedApi: android.net.ScoredNetwork#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.equals(Object)
-UnflaggedApi: android.net.ScoredNetwork#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.hashCode()
-UnflaggedApi: android.net.ScoredNetwork#toString():
-    New API must be flagged with @FlaggedApi: method android.net.ScoredNetwork.toString()
-UnflaggedApi: android.net.WebAddress#toString():
-    New API must be flagged with @FlaggedApi: method android.net.WebAddress.toString()
-UnflaggedApi: android.net.WifiKey#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.WifiKey.equals(Object)
-UnflaggedApi: android.net.WifiKey#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.WifiKey.hashCode()
-UnflaggedApi: android.net.WifiKey#toString():
-    New API must be flagged with @FlaggedApi: method android.net.WifiKey.toString()
-UnflaggedApi: android.net.metrics.ApfProgramEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.ApfProgramEvent.equals(Object)
-UnflaggedApi: android.net.metrics.ApfProgramEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.ApfProgramEvent.toString()
-UnflaggedApi: android.net.metrics.ApfStats#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.ApfStats.equals(Object)
-UnflaggedApi: android.net.metrics.ApfStats#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.ApfStats.toString()
-UnflaggedApi: android.net.metrics.DhcpClientEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpClientEvent.equals(Object)
-UnflaggedApi: android.net.metrics.DhcpClientEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpClientEvent.toString()
-UnflaggedApi: android.net.metrics.DhcpErrorEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.DhcpErrorEvent.toString()
-UnflaggedApi: android.net.metrics.IpManagerEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.IpManagerEvent.equals(Object)
-UnflaggedApi: android.net.metrics.IpManagerEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.IpManagerEvent.toString()
-UnflaggedApi: android.net.metrics.IpReachabilityEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.IpReachabilityEvent.equals(Object)
-UnflaggedApi: android.net.metrics.IpReachabilityEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.IpReachabilityEvent.toString()
-UnflaggedApi: android.net.metrics.NetworkEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.NetworkEvent.equals(Object)
-UnflaggedApi: android.net.metrics.NetworkEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.NetworkEvent.toString()
-UnflaggedApi: android.net.metrics.RaEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.RaEvent.equals(Object)
-UnflaggedApi: android.net.metrics.RaEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.RaEvent.toString()
-UnflaggedApi: android.net.metrics.ValidationProbeEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.metrics.ValidationProbeEvent.equals(Object)
-UnflaggedApi: android.net.metrics.ValidationProbeEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.net.metrics.ValidationProbeEvent.toString()
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.equals(Object)
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.hashCode()
-UnflaggedApi: android.net.vcn.VcnNetworkPolicyResult#toString():
-    New API must be flagged with @FlaggedApi: method android.net.vcn.VcnNetworkPolicyResult.toString()
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.DeviceWiphyCapabilities#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.DeviceWiphyCapabilities.toString()
-UnflaggedApi: android.net.wifi.nl80211.NativeWifiClient#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.NativeWifiClient.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.NativeWifiClient#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.NativeWifiClient.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.PnoNetwork#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.PnoNetwork#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoNetwork.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.PnoSettings#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoSettings.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.PnoSettings#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.PnoSettings.hashCode()
-UnflaggedApi: android.net.wifi.nl80211.RadioChainInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.RadioChainInfo.equals(Object)
-UnflaggedApi: android.net.wifi.nl80211.RadioChainInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.nl80211.RadioChainInfo.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetwork#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetwork.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetwork#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetwork.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.toString()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.hashCode()
 UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#isBatteryCharging():
     New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.isBatteryCharging()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.toString()
 UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder#setBatteryCharging(boolean):
     New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder.setBatteryCharging(boolean)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.equals(Object)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.hashCode()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState#toString():
-    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.toString()
-UnflaggedApi: android.os.BatterySaverPolicyConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.os.BatterySaverPolicyConfig.toString()
 UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_ONBOARDING:
     New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_ONBOARDING
-UnflaggedApi: android.os.Build.VERSION#KNOWN_CODENAMES:
-    New API must be flagged with @FlaggedApi: field android.os.Build.VERSION.KNOWN_CODENAMES
-UnflaggedApi: android.os.Build.VERSION#PREVIEW_SDK_FINGERPRINT:
-    New API must be flagged with @FlaggedApi: field android.os.Build.VERSION.PREVIEW_SDK_FINGERPRINT
-UnflaggedApi: android.os.IncidentManager.PendingReport#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.os.IncidentManager.PendingReport.equals(Object)
-UnflaggedApi: android.os.IncidentManager.PendingReport#toString():
-    New API must be flagged with @FlaggedApi: method android.os.IncidentManager.PendingReport.toString()
-UnflaggedApi: android.os.IncidentReportArgs#toString():
-    New API must be flagged with @FlaggedApi: method android.os.IncidentReportArgs.toString()
-UnflaggedApi: android.os.NewUserRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.os.NewUserRequest.toString()
-UnflaggedApi: android.os.NewUserResponse#toString():
-    New API must be flagged with @FlaggedApi: method android.os.NewUserResponse.toString()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.equals(Object)
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#hashCode():
-    New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.hashCode()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPolicy#toString():
-    New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPolicy.toString()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.equals(Object)
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#hashCode():
-    New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.hashCode()
-UnflaggedApi: android.os.PowerManager.LowPowerStandbyPortDescription#toString():
-    New API must be flagged with @FlaggedApi: method android.os.PowerManager.LowPowerStandbyPortDescription.toString()
-UnflaggedApi: android.os.ServiceSpecificException#toString():
-    New API must be flagged with @FlaggedApi: method android.os.ServiceSpecificException.toString()
-UnflaggedApi: android.os.WorkSource.WorkChain#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.equals(Object)
-UnflaggedApi: android.os.WorkSource.WorkChain#hashCode():
-    New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.hashCode()
-UnflaggedApi: android.os.WorkSource.WorkChain#toString():
-    New API must be flagged with @FlaggedApi: method android.os.WorkSource.WorkChain.toString()
-UnflaggedApi: android.os.connectivity.CellularBatteryStats#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.os.connectivity.CellularBatteryStats.equals(Object)
-UnflaggedApi: android.os.connectivity.CellularBatteryStats#hashCode():
-    New API must be flagged with @FlaggedApi: method android.os.connectivity.CellularBatteryStats.hashCode()
-UnflaggedApi: android.os.connectivity.WifiActivityEnergyInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiActivityEnergyInfo.toString()
-UnflaggedApi: android.os.connectivity.WifiBatteryStats#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiBatteryStats.equals(Object)
-UnflaggedApi: android.os.connectivity.WifiBatteryStats#hashCode():
-    New API must be flagged with @FlaggedApi: method android.os.connectivity.WifiBatteryStats.hashCode()
-UnflaggedApi: android.permission.AdminPermissionControlParams#toString():
-    New API must be flagged with @FlaggedApi: method android.permission.AdminPermissionControlParams.toString()
-UnflaggedApi: android.permission.PermissionGroupUsage#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.equals(Object)
-UnflaggedApi: android.permission.PermissionGroupUsage#hashCode():
-    New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.hashCode()
-UnflaggedApi: android.permission.PermissionGroupUsage#toString():
-    New API must be flagged with @FlaggedApi: method android.permission.PermissionGroupUsage.toString()
-UnflaggedApi: android.permission.PermissionManager.SplitPermissionInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.permission.PermissionManager.SplitPermissionInfo.equals(Object)
-UnflaggedApi: android.permission.PermissionManager.SplitPermissionInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.permission.PermissionManager.SplitPermissionInfo.hashCode()
-UnflaggedApi: android.printservice.PrintServiceInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.equals(Object)
-UnflaggedApi: android.printservice.PrintServiceInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.hashCode()
-UnflaggedApi: android.printservice.PrintServiceInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.printservice.PrintServiceInfo.toString()
-UnflaggedApi: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
-    New API must be flagged with @FlaggedApi: method android.printservice.recommendation.RecommendationService.attachBaseContext(android.content.Context)
-UnflaggedApi: android.provider.CallLog.CallComposerLoggingException#toString():
-    New API must be flagged with @FlaggedApi: method android.provider.CallLog.CallComposerLoggingException.toString()
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_ITEM_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_ITEM_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.CONTENT_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.METADATA_AUTHORITY
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync.METADATA_AUTHORITY_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#_COUNT:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync._COUNT
-UnflaggedApi: android.provider.ContactsContract.MetadataSync#_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSync._ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#ACCOUNT_NAME:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.ACCOUNT_NAME
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#ACCOUNT_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.ACCOUNT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DATA:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DATA
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DATA_SET:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DATA_SET
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#DELETED:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.DELETED
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncColumns#RAW_CONTACT_BACKUP_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncColumns.RAW_CONTACT_BACKUP_ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_ITEM_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_ITEM_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState.CONTENT_URI
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#_COUNT:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState._COUNT
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncState#_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncState._ID
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#ACCOUNT_NAME:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.ACCOUNT_NAME
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#ACCOUNT_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.ACCOUNT_TYPE
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#DATA_SET:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.DATA_SET
-UnflaggedApi: android.provider.ContactsContract.MetadataSyncStateColumns#STATE:
-    New API must be flagged with @FlaggedApi: field android.provider.ContactsContract.MetadataSyncStateColumns.STATE
-UnflaggedApi: android.provider.ContactsContract.Settings#setDefaultAccount(android.content.ContentResolver, android.accounts.Account):
-    New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.Settings.setDefaultAccount(android.content.ContentResolver,android.accounts.Account)
-UnflaggedApi: android.provider.ContactsContract.SimContacts#addSimAccount(android.content.ContentResolver, String, String, int, int):
-    New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.SimContacts.addSimAccount(android.content.ContentResolver,String,String,int,int)
-UnflaggedApi: android.provider.ContactsContract.SimContacts#removeSimAccounts(android.content.ContentResolver, int):
-    New API must be flagged with @FlaggedApi: method android.provider.ContactsContract.SimContacts.removeSimAccounts(android.content.ContentResolver,int)
-UnflaggedApi: android.provider.SearchIndexableData#toString():
-    New API must be flagged with @FlaggedApi: method android.provider.SearchIndexableData.toString()
-UnflaggedApi: android.provider.SearchIndexableResource#toString():
-    New API must be flagged with @FlaggedApi: method android.provider.SearchIndexableResource.toString()
-UnflaggedApi: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo):
-    New API must be flagged with @FlaggedApi: method android.provider.SearchIndexablesProvider.attachInfo(android.content.Context,android.content.pm.ProviderInfo)
 UnflaggedApi: android.provider.Settings#ACTION_APP_PERMISSIONS_SETTINGS:
     New API must be flagged with @FlaggedApi: field android.provider.Settings.ACTION_APP_PERMISSIONS_SETTINGS
 UnflaggedApi: android.provider.Settings.System#putString(android.content.ContentResolver, String, String, boolean, boolean):
     New API must be flagged with @FlaggedApi: method android.provider.Settings.System.putString(android.content.ContentResolver,String,String,boolean,boolean)
 UnflaggedApi: android.provider.Settings.System#resetToDefaults(android.content.ContentResolver, String):
     New API must be flagged with @FlaggedApi: method android.provider.Settings.System.resetToDefaults(android.content.ContentResolver,String)
-UnflaggedApi: android.provider.SimPhonebookContract.SimRecords#QUERY_ARG_PIN2:
-    New API must be flagged with @FlaggedApi: field android.provider.SimPhonebookContract.SimRecords.QUERY_ARG_PIN2
-UnflaggedApi: android.provider.Telephony.Carriers#APN_SET_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#CARRIER_EDITED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.CARRIER_EDITED
-UnflaggedApi: android.provider.Telephony.Carriers#EDITED_STATUS:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.EDITED_STATUS
-UnflaggedApi: android.provider.Telephony.Carriers#MATCH_ALL_APN_SET_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MATCH_ALL_APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#MAX_CONNECTIONS:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MAX_CONNECTIONS
-UnflaggedApi: android.provider.Telephony.Carriers#MODEM_PERSIST:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MODEM_PERSIST
-UnflaggedApi: android.provider.Telephony.Carriers#MTU_V4:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MTU_V4
-UnflaggedApi: android.provider.Telephony.Carriers#MTU_V6:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.MTU_V6
-UnflaggedApi: android.provider.Telephony.Carriers#NO_APN_SET_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.NO_APN_SET_ID
-UnflaggedApi: android.provider.Telephony.Carriers#TIME_LIMIT_FOR_MAX_CONNECTIONS:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS
-UnflaggedApi: android.provider.Telephony.Carriers#UNEDITED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.UNEDITED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_DELETED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_DELETED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_EDITABLE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_EDITABLE
-UnflaggedApi: android.provider.Telephony.Carriers#USER_EDITED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_EDITED
-UnflaggedApi: android.provider.Telephony.Carriers#USER_VISIBLE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.USER_VISIBLE
-UnflaggedApi: android.provider.Telephony.Carriers#WAIT_TIME_RETRY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Carriers.WAIT_TIME_RETRY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts:
-    New API must be flagged with @FlaggedApi: class android.provider.Telephony.CellBroadcasts
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#AUTHORITY_LEGACY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.AUTHORITY_LEGACY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#AUTHORITY_LEGACY_URI:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.AUTHORITY_LEGACY_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CALL_METHOD_GET_PREFERENCE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CALL_METHOD_GET_PREFERENCE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CID:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_CATEGORY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_CATEGORY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_CERTAINTY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_CERTAINTY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_MESSAGE_CLASS:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_RESPONSE_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_SEVERITY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_SEVERITY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CMAS_URGENCY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CMAS_URGENCY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#CONTENT_URI:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.CONTENT_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DATA_CODING_SCHEME:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DATA_CODING_SCHEME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DEFAULT_SORT_ORDER:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DEFAULT_SORT_ORDER
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#DELIVERY_TIME:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.DELIVERY_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#ETWS_IS_PRIMARY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.ETWS_IS_PRIMARY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#ETWS_WARNING_TYPE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.ETWS_WARNING_TYPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#GEOGRAPHICAL_SCOPE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#GEOMETRIES:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.GEOMETRIES
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LAC:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LAC
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LANGUAGE_CODE:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LANGUAGE_CODE
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#LOCATION_CHECK_TIME:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.LOCATION_CHECK_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MAXIMUM_WAIT_TIME:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MAXIMUM_WAIT_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_BODY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_BODY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_BROADCASTED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_BROADCASTED
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_DISPLAYED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_DISPLAYED
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_FORMAT:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_FORMAT
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_HISTORY_URI:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_HISTORY_URI
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_PRIORITY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_PRIORITY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#MESSAGE_READ:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.MESSAGE_READ
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#PLMN:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.PLMN
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#RECEIVED_TIME:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.RECEIVED_TIME
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SERIAL_NUMBER:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SERIAL_NUMBER
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SERVICE_CATEGORY:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SERVICE_CATEGORY
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SLOT_INDEX:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SLOT_INDEX
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#SUBSCRIPTION_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.SUBSCRIPTION_ID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#_COUNT:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts._COUNT
-UnflaggedApi: android.provider.Telephony.CellBroadcasts#_ID:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts._ID
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference:
-    New API must be flagged with @FlaggedApi: class android.provider.Telephony.CellBroadcasts.Preference
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_ALERT_VIBRATION_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_ALERT_VIBRATION_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_AREA_UPDATE_INFO_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_AREA_UPDATE_INFO_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_AMBER_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_AMBER_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_EXTREME_THREAT_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_EXTREME_THREAT_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_PRESIDENTIAL_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_PRESIDENTIAL_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_CMAS_SEVERE_THREAT_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_CMAS_SEVERE_THREAT_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_EMERGENCY_PERF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_EMERGENCY_PERF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_PUBLIC_SAFETY_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_PUBLIC_SAFETY_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_STATE_LOCAL_TEST_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_STATE_LOCAL_TEST_PREF
-UnflaggedApi: android.provider.Telephony.CellBroadcasts.Preference#ENABLE_TEST_ALERT_PREF:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.CellBroadcasts.Preference.ENABLE_TEST_ALERT_PREF
-UnflaggedApi: android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED:
-    New API must be flagged with @FlaggedApi: field android.provider.Telephony.Sms.Intents.ACTION_SMS_EMERGENCY_CB_RECEIVED
-UnflaggedApi: android.service.ambientcontext.AmbientContextDetectionResult#toString():
-    New API must be flagged with @FlaggedApi: method android.service.ambientcontext.AmbientContextDetectionResult.toString()
-UnflaggedApi: android.service.ambientcontext.AmbientContextDetectionServiceStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.service.ambientcontext.AmbientContextDetectionServiceStatus.toString()
-UnflaggedApi: android.service.appprediction.AppPredictionService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.appprediction.AppPredictionService.onCreate()
-UnflaggedApi: android.service.assist.classification.FieldClassificationRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationRequest.toString()
-UnflaggedApi: android.service.assist.classification.FieldClassificationResponse#toString():
-    New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationResponse.toString()
-UnflaggedApi: android.service.assist.classification.FieldClassificationService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.assist.classification.FieldClassificationService.onCreate()
-UnflaggedApi: android.service.autofill.AutofillFieldClassificationService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.autofill.AutofillFieldClassificationService.onCreate()
-UnflaggedApi: android.service.autofill.Dataset.Builder#setContent(android.view.autofill.AutofillId, android.content.ClipData):
-    New API must be flagged with @FlaggedApi: method android.service.autofill.Dataset.Builder.setContent(android.view.autofill.AutofillId,android.content.ClipData)
-UnflaggedApi: android.service.autofill.augmented.AugmentedAutofillService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.AugmentedAutofillService.onCreate()
-UnflaggedApi: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent):
-    New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.AugmentedAutofillService.onUnbind(android.content.Intent)
-UnflaggedApi: android.service.autofill.augmented.FillRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.FillRequest.toString()
-UnflaggedApi: android.service.autofill.augmented.FillWindow#finalize():
-    New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.FillWindow.finalize()
-UnflaggedApi: android.service.autofill.augmented.PresentationParams.Area#toString():
-    New API must be flagged with @FlaggedApi: method android.service.autofill.augmented.PresentationParams.Area.toString()
-UnflaggedApi: android.service.cloudsearch.CloudSearchService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.cloudsearch.CloudSearchService.onCreate()
-UnflaggedApi: android.service.contentcapture.ActivityEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.service.contentcapture.ActivityEvent.toString()
-UnflaggedApi: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
-    New API must be flagged with @FlaggedApi: method android.service.contentcapture.ContentCaptureService.dump(java.io.FileDescriptor,java.io.PrintWriter,String[])
-UnflaggedApi: android.service.contentcapture.ContentCaptureService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.contentcapture.ContentCaptureService.onCreate()
-UnflaggedApi: android.service.contentsuggestions.ContentSuggestionsService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.contentsuggestions.ContentSuggestionsService.onCreate()
-UnflaggedApi: android.service.displayhash.DisplayHashParams#toString():
-    New API must be flagged with @FlaggedApi: method android.service.displayhash.DisplayHashParams.toString()
-UnflaggedApi: android.service.displayhash.DisplayHashingService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.displayhash.DisplayHashingService.onCreate()
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.equals(Object)
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.hashCode()
-UnflaggedApi: android.service.euicc.EuiccProfileInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccProfileInfo.toString()
-UnflaggedApi: android.service.euicc.EuiccService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccService.onCreate()
-UnflaggedApi: android.service.euicc.EuiccService#onDestroy():
-    New API must be flagged with @FlaggedApi: method android.service.euicc.EuiccService.onDestroy()
-UnflaggedApi: android.service.games.CreateGameSessionRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.equals(Object)
-UnflaggedApi: android.service.games.CreateGameSessionRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.hashCode()
-UnflaggedApi: android.service.games.CreateGameSessionRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.service.games.CreateGameSessionRequest.toString()
-UnflaggedApi: android.service.games.GameSessionService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.games.GameSessionService.onCreate()
-UnflaggedApi: android.service.games.GameStartedEvent#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.equals(Object)
-UnflaggedApi: android.service.games.GameStartedEvent#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.hashCode()
-UnflaggedApi: android.service.games.GameStartedEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.service.games.GameStartedEvent.toString()
-UnflaggedApi: android.service.notification.Adjustment#toString():
-    New API must be flagged with @FlaggedApi: method android.service.notification.Adjustment.toString()
-UnflaggedApi: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
-    New API must be flagged with @FlaggedApi: method android.service.notification.NotificationAssistantService.attachBaseContext(android.content.Context)
-UnflaggedApi: android.service.notification.NotificationStats#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.equals(Object)
-UnflaggedApi: android.service.notification.NotificationStats#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.hashCode()
-UnflaggedApi: android.service.notification.NotificationStats#toString():
-    New API must be flagged with @FlaggedApi: method android.service.notification.NotificationStats.toString()
-UnflaggedApi: android.service.notification.SnoozeCriterion#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.notification.SnoozeCriterion.equals(Object)
-UnflaggedApi: android.service.notification.SnoozeCriterion#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.notification.SnoozeCriterion.hashCode()
-UnflaggedApi: android.service.resolver.ResolverRankerService#onDestroy():
-    New API must be flagged with @FlaggedApi: method android.service.resolver.ResolverRankerService.onDestroy()
-UnflaggedApi: android.service.resolver.ResolverTarget#toString():
-    New API must be flagged with @FlaggedApi: method android.service.resolver.ResolverTarget.toString()
-UnflaggedApi: android.service.rotationresolver.RotationResolutionRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.service.rotationresolver.RotationResolutionRequest.toString()
-UnflaggedApi: android.service.search.SearchUiService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.search.SearchUiService.onCreate()
-UnflaggedApi: android.service.smartspace.SmartspaceService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.smartspace.SmartspaceService.onCreate()
-UnflaggedApi: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent):
-    New API must be flagged with @FlaggedApi: method android.service.textclassifier.TextClassifierService.onUnbind(android.content.Intent)
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.equals(Object)
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.hashCode()
-UnflaggedApi: android.service.timezone.TimeZoneProviderStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderStatus.toString()
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.equals(Object)
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.hashCode()
-UnflaggedApi: android.service.timezone.TimeZoneProviderSuggestion#toString():
-    New API must be flagged with @FlaggedApi: method android.service.timezone.TimeZoneProviderSuggestion.toString()
-UnflaggedApi: android.service.translation.TranslationService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.translation.TranslationService.onCreate()
-UnflaggedApi: android.service.trust.TrustAgentService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.trust.TrustAgentService.onCreate()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.equals(Object)
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.hashCode()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.equals(Object)
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.hashCode()
-UnflaggedApi: android.service.voice.AlwaysOnHotwordDetector.ModelParamRange#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.AlwaysOnHotwordDetector.ModelParamRange.toString()
-UnflaggedApi: android.service.voice.HotwordAudioStream#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.equals(Object)
-UnflaggedApi: android.service.voice.HotwordAudioStream#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.hashCode()
-UnflaggedApi: android.service.voice.HotwordAudioStream#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordAudioStream.toString()
-UnflaggedApi: android.service.voice.HotwordDetectedResult#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.equals(Object)
-UnflaggedApi: android.service.voice.HotwordDetectedResult#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.hashCode()
-UnflaggedApi: android.service.voice.HotwordDetectedResult#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectedResult.toString()
-UnflaggedApi: android.service.voice.HotwordDetectionService#getSystemService(String):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectionService.getSystemService(String)
-UnflaggedApi: android.service.voice.HotwordDetectionServiceFailure#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordDetectionServiceFailure.toString()
-UnflaggedApi: android.service.voice.HotwordRejectedResult#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.equals(Object)
-UnflaggedApi: android.service.voice.HotwordRejectedResult#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.hashCode()
-UnflaggedApi: android.service.voice.HotwordRejectedResult#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordRejectedResult.toString()
 UnflaggedApi: android.service.voice.HotwordTrainingAudio:
     New API must be flagged with @FlaggedApi: class android.service.voice.HotwordTrainingAudio
 UnflaggedApi: android.service.voice.HotwordTrainingAudio#CONTENTS_FILE_DESCRIPTOR:
@@ -2487,8 +271,6 @@
     New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingAudio.PARCELABLE_WRITE_RETURN_VALUE
 UnflaggedApi: android.service.voice.HotwordTrainingAudio#describeContents():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.equals(Object)
 UnflaggedApi: android.service.voice.HotwordTrainingAudio#getAudioFormat():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getAudioFormat()
 UnflaggedApi: android.service.voice.HotwordTrainingAudio#getAudioType():
@@ -2497,10 +279,6 @@
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getHotwordAudio()
 UnflaggedApi: android.service.voice.HotwordTrainingAudio#getHotwordOffsetMillis():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.getHotwordOffsetMillis()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.hashCode()
-UnflaggedApi: android.service.voice.HotwordTrainingAudio#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.toString()
 UnflaggedApi: android.service.voice.HotwordTrainingAudio#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder:
@@ -2513,8 +291,6 @@
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setAudioFormat(android.media.AudioFormat)
 UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setAudioType(int):
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setAudioType(int)
-UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setHotwordAudio(byte...):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setHotwordAudio(byte...)
 UnflaggedApi: android.service.voice.HotwordTrainingAudio.Builder#setHotwordOffsetMillis(int):
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingAudio.Builder.setHotwordOffsetMillis(int)
 UnflaggedApi: android.service.voice.HotwordTrainingData:
@@ -2537,18 +313,12 @@
     New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingData.TIMEOUT_STAGE_VERY_EARLY
 UnflaggedApi: android.service.voice.HotwordTrainingData#describeContents():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingData#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.equals(Object)
 UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataSize():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataSize()
 UnflaggedApi: android.service.voice.HotwordTrainingData#getTimeoutStage():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTimeoutStage()
 UnflaggedApi: android.service.voice.HotwordTrainingData#getTrainingAudios():
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTrainingAudios()
-UnflaggedApi: android.service.voice.HotwordTrainingData#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.hashCode()
-UnflaggedApi: android.service.voice.HotwordTrainingData#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.toString()
 UnflaggedApi: android.service.voice.HotwordTrainingData#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.service.voice.HotwordTrainingData.Builder:
@@ -2563,330 +333,10 @@
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTimeoutStage(int)
 UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>):
     New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>)
-UnflaggedApi: android.service.voice.SoundTriggerFailure#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.SoundTriggerFailure.toString()
-UnflaggedApi: android.service.voice.VisualQueryDetectionService#getSystemService(String):
-    New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionService.getSystemService(String)
-UnflaggedApi: android.service.voice.VisualQueryDetectionService#openFileInput(String):
-    New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionService.openFileInput(String)
-UnflaggedApi: android.service.voice.VisualQueryDetectionServiceFailure#toString():
-    New API must be flagged with @FlaggedApi: method android.service.voice.VisualQueryDetectionServiceFailure.toString()
-UnflaggedApi: android.service.wallpaper.WallpaperService.Engine#isInAmbientMode():
-    New API must be flagged with @FlaggedApi: method android.service.wallpaper.WallpaperService.Engine.isInAmbientMode()
-UnflaggedApi: android.service.wallpaper.WallpaperService.Engine#onAmbientModeChanged(boolean, long):
-    New API must be flagged with @FlaggedApi: method android.service.wallpaper.WallpaperService.Engine.onAmbientModeChanged(boolean,long)
-UnflaggedApi: android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService#onCreate():
-    New API must be flagged with @FlaggedApi: method android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService.onCreate()
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.equals(Object)
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.hashCode()
-UnflaggedApi: android.service.watchdog.ExplicitHealthCheckService.PackageConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.service.watchdog.ExplicitHealthCheckService.PackageConfig.toString()
-UnflaggedApi: android.telecom.AudioState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telecom.AudioState.equals(Object)
-UnflaggedApi: android.telecom.AudioState#toString():
-    New API must be flagged with @FlaggedApi: method android.telecom.AudioState.toString()
-UnflaggedApi: android.telecom.BluetoothCallQualityReport#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telecom.BluetoothCallQualityReport.equals(Object)
-UnflaggedApi: android.telecom.BluetoothCallQualityReport#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telecom.BluetoothCallQualityReport.hashCode()
-UnflaggedApi: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
-    New API must be flagged with @FlaggedApi: method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
-UnflaggedApi: android.telecom.Connection.CallFilteringCompletionInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telecom.Connection.CallFilteringCompletionInfo.toString()
 UnflaggedApi: android.telecom.StreamingCall#EXTRA_CALL_ID:
     New API must be flagged with @FlaggedApi: field android.telecom.StreamingCall.EXTRA_CALL_ID
-UnflaggedApi: android.telephony.CallAttributes#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.equals(Object)
-UnflaggedApi: android.telephony.CallAttributes#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.hashCode()
-UnflaggedApi: android.telephony.CallAttributes#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CallAttributes.toString()
-UnflaggedApi: android.telephony.CallQuality#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.equals(Object)
-UnflaggedApi: android.telephony.CallQuality#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.hashCode()
-UnflaggedApi: android.telephony.CallQuality#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CallQuality.toString()
-UnflaggedApi: android.telephony.CallState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.CallState.equals(Object)
-UnflaggedApi: android.telephony.CallState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.CallState.hashCode()
-UnflaggedApi: android.telephony.CallState#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CallState.toString()
-UnflaggedApi: android.telephony.CarrierRestrictionRules#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CarrierRestrictionRules.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.Circle#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.Circle.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.LatLng#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.LatLng.toString()
-UnflaggedApi: android.telephony.CbGeoUtils.Polygon#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CbGeoUtils.Polygon.toString()
-UnflaggedApi: android.telephony.CellBroadcastIdRange#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.equals(Object)
-UnflaggedApi: android.telephony.CellBroadcastIdRange#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.hashCode()
-UnflaggedApi: android.telephony.CellBroadcastIdRange#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.CellBroadcastIdRange.toString()
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.equals(Object)
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.hashCode()
-UnflaggedApi: android.telephony.DataSpecificRegistrationInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.DataSpecificRegistrationInfo.toString()
-UnflaggedApi: android.telephony.DataThrottlingRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.DataThrottlingRequest.toString()
-UnflaggedApi: android.telephony.ImsiEncryptionInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ImsiEncryptionInfo.toString()
-UnflaggedApi: android.telephony.LinkCapacityEstimate#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.equals(Object)
-UnflaggedApi: android.telephony.LinkCapacityEstimate#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.hashCode()
-UnflaggedApi: android.telephony.LinkCapacityEstimate#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.LinkCapacityEstimate.toString()
-UnflaggedApi: android.telephony.LteVopsSupportInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.LteVopsSupportInfo.toString()
-UnflaggedApi: android.telephony.ModemActivityInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.equals(Object)
-UnflaggedApi: android.telephony.ModemActivityInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.hashCode()
-UnflaggedApi: android.telephony.ModemActivityInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ModemActivityInfo.toString()
 UnflaggedApi: android.telephony.NetworkRegistrationInfo.Builder#setIsNonTerrestrialNetwork(boolean):
     New API must be flagged with @FlaggedApi: method android.telephony.NetworkRegistrationInfo.Builder.setIsNonTerrestrialNetwork(boolean)
-UnflaggedApi: android.telephony.NetworkService#onUnbind(android.content.Intent):
-    New API must be flagged with @FlaggedApi: method android.telephony.NetworkService.onUnbind(android.content.Intent)
-UnflaggedApi: android.telephony.NrVopsSupportInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.NrVopsSupportInfo.toString()
-UnflaggedApi: android.telephony.PhoneCapability#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.equals(Object)
-UnflaggedApi: android.telephony.PhoneCapability#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.hashCode()
-UnflaggedApi: android.telephony.PhoneCapability#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.PhoneCapability.toString()
-UnflaggedApi: android.telephony.PhoneNumberRange#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.equals(Object)
-UnflaggedApi: android.telephony.PhoneNumberRange#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.hashCode()
-UnflaggedApi: android.telephony.PhoneNumberRange#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.PhoneNumberRange.toString()
-UnflaggedApi: android.telephony.PinResult#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.PinResult.equals(Object)
-UnflaggedApi: android.telephony.PinResult#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.PinResult.hashCode()
-UnflaggedApi: android.telephony.PinResult#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.PinResult.toString()
-UnflaggedApi: android.telephony.PreciseCallState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.equals(Object)
-UnflaggedApi: android.telephony.PreciseCallState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.hashCode()
-UnflaggedApi: android.telephony.PreciseCallState#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.PreciseCallState.toString()
-UnflaggedApi: android.telephony.SmsCbCmasInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.SmsCbCmasInfo.toString()
-UnflaggedApi: android.telephony.SmsCbEtwsInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.SmsCbEtwsInfo.toString()
-UnflaggedApi: android.telephony.SmsCbLocation#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.equals(Object)
-UnflaggedApi: android.telephony.SmsCbLocation#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.hashCode()
-UnflaggedApi: android.telephony.SmsCbLocation#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.SmsCbLocation.toString()
-UnflaggedApi: android.telephony.SmsCbMessage#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.SmsCbMessage.toString()
-UnflaggedApi: android.telephony.TelephonyHistogram#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.TelephonyHistogram.toString()
-UnflaggedApi: android.telephony.TelephonyManager.ModemActivityInfoException#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.TelephonyManager.ModemActivityInfoException.toString()
-UnflaggedApi: android.telephony.ThermalMitigationRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ThermalMitigationRequest.toString()
-UnflaggedApi: android.telephony.UiccAccessRule#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.equals(Object)
-UnflaggedApi: android.telephony.UiccAccessRule#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.hashCode()
-UnflaggedApi: android.telephony.UiccAccessRule#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccAccessRule.toString()
-UnflaggedApi: android.telephony.UiccSlotInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.equals(Object)
-UnflaggedApi: android.telephony.UiccSlotInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.hashCode()
-UnflaggedApi: android.telephony.UiccSlotInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotInfo.toString()
-UnflaggedApi: android.telephony.UiccSlotMapping#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.equals(Object)
-UnflaggedApi: android.telephony.UiccSlotMapping#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.hashCode()
-UnflaggedApi: android.telephony.UiccSlotMapping#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.UiccSlotMapping.toString()
-UnflaggedApi: android.telephony.VopsSupportInfo#writeToParcel(android.os.Parcel, int):
-    New API must be flagged with @FlaggedApi: method android.telephony.VopsSupportInfo.writeToParcel(android.os.Parcel,int)
-UnflaggedApi: android.telephony.cdma.CdmaSmsCbProgramData#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.cdma.CdmaSmsCbProgramData.toString()
-UnflaggedApi: android.telephony.data.DataCallResponse#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.equals(Object)
-UnflaggedApi: android.telephony.data.DataCallResponse#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.hashCode()
-UnflaggedApi: android.telephony.data.DataCallResponse#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataCallResponse.toString()
-UnflaggedApi: android.telephony.data.DataProfile#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.equals(Object)
-UnflaggedApi: android.telephony.data.DataProfile#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.hashCode()
-UnflaggedApi: android.telephony.data.DataProfile#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataProfile.toString()
-UnflaggedApi: android.telephony.data.DataService#onDestroy():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataService.onDestroy()
-UnflaggedApi: android.telephony.data.DataService#onUnbind(android.content.Intent):
-    New API must be flagged with @FlaggedApi: method android.telephony.data.DataService.onUnbind(android.content.Intent)
-UnflaggedApi: android.telephony.data.EpsBearerQosSessionAttributes#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.data.EpsBearerQosSessionAttributes.equals(Object)
-UnflaggedApi: android.telephony.data.EpsBearerQosSessionAttributes#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.EpsBearerQosSessionAttributes.hashCode()
-UnflaggedApi: android.telephony.data.NrQosSessionAttributes#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.data.NrQosSessionAttributes.equals(Object)
-UnflaggedApi: android.telephony.data.NrQosSessionAttributes#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.NrQosSessionAttributes.hashCode()
-UnflaggedApi: android.telephony.data.ThrottleStatus#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.equals(Object)
-UnflaggedApi: android.telephony.data.ThrottleStatus#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.hashCode()
-UnflaggedApi: android.telephony.data.ThrottleStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.data.ThrottleStatus.toString()
-UnflaggedApi: android.telephony.euicc.EuiccNotification#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.equals(Object)
-UnflaggedApi: android.telephony.euicc.EuiccNotification#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.hashCode()
-UnflaggedApi: android.telephony.euicc.EuiccNotification#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccNotification.toString()
-UnflaggedApi: android.telephony.euicc.EuiccRulesAuthTable#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.euicc.EuiccRulesAuthTable.equals(Object)
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.equals(Object)
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.hashCode()
-UnflaggedApi: android.telephony.gba.UaSecurityProtocolIdentifier#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.gba.UaSecurityProtocolIdentifier.toString()
-UnflaggedApi: android.telephony.ims.AudioCodecAttributes#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.AudioCodecAttributes.toString()
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.equals(Object)
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.hashCode()
-UnflaggedApi: android.telephony.ims.DelegateRegistrationState#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRegistrationState.toString()
-UnflaggedApi: android.telephony.ims.DelegateRequest#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.equals(Object)
-UnflaggedApi: android.telephony.ims.DelegateRequest#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.hashCode()
-UnflaggedApi: android.telephony.ims.DelegateRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.DelegateRequest.toString()
-UnflaggedApi: android.telephony.ims.FeatureTagState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.equals(Object)
-UnflaggedApi: android.telephony.ims.FeatureTagState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.hashCode()
-UnflaggedApi: android.telephony.ims.FeatureTagState#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.FeatureTagState.toString()
-UnflaggedApi: android.telephony.ims.ImsCallForwardInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsCallForwardInfo.toString()
-UnflaggedApi: android.telephony.ims.ImsCallProfile#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsCallProfile.toString()
-UnflaggedApi: android.telephony.ims.ImsConferenceState#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsConferenceState.toString()
-UnflaggedApi: android.telephony.ims.ImsExternalCallState#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsExternalCallState.toString()
-UnflaggedApi: android.telephony.ims.ImsMmTelManager.RegistrationCallback#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsMmTelManager.RegistrationCallback.onTechnologyChangeFailed(int,android.telephony.ims.ImsReasonInfo)
-UnflaggedApi: android.telephony.ims.ImsMmTelManager.RegistrationCallback#onUnregistered(android.telephony.ims.ImsReasonInfo):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsMmTelManager.RegistrationCallback.onUnregistered(android.telephony.ims.ImsReasonInfo)
-UnflaggedApi: android.telephony.ims.ImsSsData#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSsData.toString()
-UnflaggedApi: android.telephony.ims.ImsSsInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSsInfo.toString()
-UnflaggedApi: android.telephony.ims.ImsStreamMediaProfile#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsStreamMediaProfile.toString()
-UnflaggedApi: android.telephony.ims.ImsSuppServiceNotification#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.ImsSuppServiceNotification.toString()
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.equals(Object)
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.hashCode()
-UnflaggedApi: android.telephony.ims.MediaQualityStatus#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaQualityStatus.toString()
-UnflaggedApi: android.telephony.ims.MediaThreshold#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.equals(Object)
-UnflaggedApi: android.telephony.ims.MediaThreshold#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.hashCode()
-UnflaggedApi: android.telephony.ims.MediaThreshold#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.MediaThreshold.toString()
-UnflaggedApi: android.telephony.ims.PublishAttributes#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.equals(Object)
-UnflaggedApi: android.telephony.ims.PublishAttributes#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.hashCode()
-UnflaggedApi: android.telephony.ims.PublishAttributes#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.PublishAttributes.toString()
-UnflaggedApi: android.telephony.ims.RcsClientConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsClientConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.RcsClientConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsClientConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.RcsContactPresenceTuple#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactPresenceTuple.toString()
-UnflaggedApi: android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.toString()
-UnflaggedApi: android.telephony.ims.RcsContactUceCapability#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RcsContactUceCapability.toString()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.equals(Object)
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.hashCode()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtension#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtension.toString()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.equals(Object)
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.hashCode()
-UnflaggedApi: android.telephony.ims.RtpHeaderExtensionType#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.RtpHeaderExtensionType.toString()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.toString()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.hashCode()
-UnflaggedApi: android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration.toString()
-UnflaggedApi: android.telephony.ims.SipDialogState#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDialogState.equals(Object)
-UnflaggedApi: android.telephony.ims.SipDialogState#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipDialogState.hashCode()
-UnflaggedApi: android.telephony.ims.SipMessage#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.equals(Object)
-UnflaggedApi: android.telephony.ims.SipMessage#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.hashCode()
-UnflaggedApi: android.telephony.ims.SipMessage#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SipMessage.toString()
-UnflaggedApi: android.telephony.ims.SrvccCall#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.equals(Object)
-UnflaggedApi: android.telephony.ims.SrvccCall#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.hashCode()
-UnflaggedApi: android.telephony.ims.SrvccCall#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.SrvccCall.toString()
-UnflaggedApi: android.telephony.ims.feature.CapabilityChangeRequest#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.feature.CapabilityChangeRequest.toString()
-UnflaggedApi: android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair.toString()
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.equals(Object)
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.hashCode()
-UnflaggedApi: android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair.toString()
-UnflaggedApi: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
-    New API must be flagged with @FlaggedApi: method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
 UnflaggedApi: android.telephony.mbms.vendor.MbmsDownloadServiceBase#DESCRIPTOR:
     New API must be flagged with @FlaggedApi: field android.telephony.mbms.vendor.MbmsDownloadServiceBase.DESCRIPTOR
 UnflaggedApi: android.telephony.mbms.vendor.MbmsStreamingServiceBase#DESCRIPTOR:
@@ -2901,18 +351,12 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.AntennaDirection.PARCELABLE_WRITE_RETURN_VALUE
 UnflaggedApi: android.telephony.satellite.AntennaDirection#describeContents():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.describeContents()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.equals(Object)
 UnflaggedApi: android.telephony.satellite.AntennaDirection#getX():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getX()
 UnflaggedApi: android.telephony.satellite.AntennaDirection#getY():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getY()
 UnflaggedApi: android.telephony.satellite.AntennaDirection#getZ():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.getZ()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.hashCode()
-UnflaggedApi: android.telephony.satellite.AntennaDirection#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.toString()
 UnflaggedApi: android.telephony.satellite.AntennaDirection#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaDirection.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.telephony.satellite.AntennaPosition:
@@ -2925,16 +369,10 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.AntennaPosition.PARCELABLE_WRITE_RETURN_VALUE
 UnflaggedApi: android.telephony.satellite.AntennaPosition#describeContents():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.describeContents()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.equals(Object)
 UnflaggedApi: android.telephony.satellite.AntennaPosition#getAntennaDirection():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.getAntennaDirection()
 UnflaggedApi: android.telephony.satellite.AntennaPosition#getSuggestedHoldPosition():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.getSuggestedHoldPosition()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.hashCode()
-UnflaggedApi: android.telephony.satellite.AntennaPosition#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.toString()
 UnflaggedApi: android.telephony.satellite.AntennaPosition#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.AntennaPosition.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.telephony.satellite.PointingInfo:
@@ -2947,16 +385,10 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.PointingInfo.PARCELABLE_WRITE_RETURN_VALUE
 UnflaggedApi: android.telephony.satellite.PointingInfo#describeContents():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.describeContents()
-UnflaggedApi: android.telephony.satellite.PointingInfo#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.equals(Object)
 UnflaggedApi: android.telephony.satellite.PointingInfo#getSatelliteAzimuthDegrees():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.getSatelliteAzimuthDegrees()
 UnflaggedApi: android.telephony.satellite.PointingInfo#getSatelliteElevationDegrees():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.getSatelliteElevationDegrees()
-UnflaggedApi: android.telephony.satellite.PointingInfo#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.hashCode()
-UnflaggedApi: android.telephony.satellite.PointingInfo#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.toString()
 UnflaggedApi: android.telephony.satellite.PointingInfo#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.PointingInfo.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities:
@@ -2969,20 +401,14 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteCapabilities.PARCELABLE_WRITE_RETURN_VALUE
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#describeContents():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.describeContents()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.equals(Object)
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getAntennaPositionMap():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getAntennaPositionMap()
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getMaxBytesPerOutgoingDatagram():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getMaxBytesPerOutgoingDatagram()
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#getSupportedRadioTechnologies():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.getSupportedRadioTechnologies()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#hashCode():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.hashCode()
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#isPointingRequired():
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.isPointingRequired()
-UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#toString():
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.toString()
 UnflaggedApi: android.telephony.satellite.SatelliteCapabilities#writeToParcel(android.os.Parcel, int):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteCapabilities.writeToParcel(android.os.Parcel,int)
 UnflaggedApi: android.telephony.satellite.SatelliteDatagram:
@@ -3037,8 +463,6 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY
 UnflaggedApi: android.telephony.satellite.SatelliteManager#NT_RADIO_TECHNOLOGY_UNKNOWN:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ACCESS_BARRED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ACCESS_BARRED
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED:
@@ -3057,20 +481,6 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ERROR:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_ERROR_NONE:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_ARGUMENTS:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_ARGUMENTS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_MODEM_STATE:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_MODEM_STATE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_INVALID_TELEPHONY_STATE:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_BUSY:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_BUSY
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_ERROR:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_ERROR
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
@@ -3085,28 +495,6 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_MODEM_STATE_UNKNOWN:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NETWORK_ERROR:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NETWORK_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NETWORK_TIMEOUT:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NETWORK_TIMEOUT
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_AUTHORIZED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_AUTHORIZED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_REACHABLE:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_REACHABLE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NOT_SUPPORTED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NOT_SUPPORTED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_NO_RESOURCES:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_NO_RESOURCES
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RADIO_NOT_AVAILABLE:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_ABORTED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_ABORTED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_FAILED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_FAILED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_IN_PROGRESS:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_REQUEST_NOT_SUPPORTED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_ACCESS_BARRED:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_ERROR:
@@ -3153,14 +541,6 @@
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS
 UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_RESULT_SUCCESS:
     New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVER_ERROR:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVER_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_ERROR:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_ERROR
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_NOT_PROVISIONED:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED
-UnflaggedApi: android.telephony.satellite.SatelliteManager#SATELLITE_SERVICE_PROVISION_IN_PROGRESS:
-    New API must be flagged with @FlaggedApi: field android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS
 UnflaggedApi: android.telephony.satellite.SatelliteManager#deprovisionSatelliteService(String, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.deprovisionSatelliteService(String,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
 UnflaggedApi: android.telephony.satellite.SatelliteManager#pollPendingSatelliteDatagrams(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
@@ -3225,141 +605,3 @@
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteTransmissionUpdateCallback.onSatellitePositionChanged(android.telephony.satellite.PointingInfo)
 UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onSendDatagramStateChanged(int, int, int):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteTransmissionUpdateCallback.onSendDatagramStateChanged(int,int,int)
-UnflaggedApi: android.text.FontConfig#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.equals(Object)
-UnflaggedApi: android.text.FontConfig#hashCode():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.hashCode()
-UnflaggedApi: android.text.FontConfig#toString():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.toString()
-UnflaggedApi: android.text.FontConfig.Alias#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.equals(Object)
-UnflaggedApi: android.text.FontConfig.Alias#hashCode():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.hashCode()
-UnflaggedApi: android.text.FontConfig.Alias#toString():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.Alias.toString()
-UnflaggedApi: android.text.FontConfig.Font#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.equals(Object)
-UnflaggedApi: android.text.FontConfig.Font#hashCode():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.hashCode()
-UnflaggedApi: android.text.FontConfig.Font#toString():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.Font.toString()
-UnflaggedApi: android.text.FontConfig.FontFamily#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.equals(Object)
-UnflaggedApi: android.text.FontConfig.FontFamily#hashCode():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.hashCode()
-UnflaggedApi: android.text.FontConfig.FontFamily#toString():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.FontFamily.toString()
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.equals(Object)
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#hashCode():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.hashCode()
-UnflaggedApi: android.text.FontConfig.NamedFamilyList#toString():
-    New API must be flagged with @FlaggedApi: method android.text.FontConfig.NamedFamilyList.toString()
-UnflaggedApi: android.view.contentcapture.ContentCaptureEvent#toString():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ContentCaptureEvent.toString()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillHints():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillHints()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillId():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillId()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillOptions():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillOptions()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillType():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getAutofillValue():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getAutofillValue()
-UnflaggedApi: android.view.contentcapture.ViewNode#getClassName():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getClassName()
-UnflaggedApi: android.view.contentcapture.ViewNode#getContentDescription():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getContentDescription()
-UnflaggedApi: android.view.contentcapture.ViewNode#getExtras():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getExtras()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHeight():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHeight()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHint():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHint()
-UnflaggedApi: android.view.contentcapture.ViewNode#getHintIdEntry():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getHintIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getId():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getId()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdEntry():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdPackage():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdPackage()
-UnflaggedApi: android.view.contentcapture.ViewNode#getIdType():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getIdType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getInputType():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getInputType()
-UnflaggedApi: android.view.contentcapture.ViewNode#getLeft():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getLeft()
-UnflaggedApi: android.view.contentcapture.ViewNode#getLocaleList():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getLocaleList()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMaxTextEms():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMaxTextEms()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMaxTextLength():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMaxTextLength()
-UnflaggedApi: android.view.contentcapture.ViewNode#getMinTextEms():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getMinTextEms()
-UnflaggedApi: android.view.contentcapture.ViewNode#getReceiveContentMimeTypes():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getReceiveContentMimeTypes()
-UnflaggedApi: android.view.contentcapture.ViewNode#getScrollX():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getScrollX()
-UnflaggedApi: android.view.contentcapture.ViewNode#getScrollY():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getScrollY()
-UnflaggedApi: android.view.contentcapture.ViewNode#getText():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getText()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextBackgroundColor():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextBackgroundColor()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextColor():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextColor()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextIdEntry():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextIdEntry()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextLineBaselines():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextLineBaselines()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextLineCharOffsets():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextLineCharOffsets()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSelectionEnd():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSelectionEnd()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSelectionStart():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSelectionStart()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextSize():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextSize()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTextStyle():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTextStyle()
-UnflaggedApi: android.view.contentcapture.ViewNode#getTop():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getTop()
-UnflaggedApi: android.view.contentcapture.ViewNode#getVisibility():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getVisibility()
-UnflaggedApi: android.view.contentcapture.ViewNode#getWidth():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.getWidth()
-UnflaggedApi: android.view.contentcapture.ViewNode#isAccessibilityFocused():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isAccessibilityFocused()
-UnflaggedApi: android.view.contentcapture.ViewNode#isActivated():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isActivated()
-UnflaggedApi: android.view.contentcapture.ViewNode#isAssistBlocked():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isAssistBlocked()
-UnflaggedApi: android.view.contentcapture.ViewNode#isCheckable():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isCheckable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isChecked():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isChecked()
-UnflaggedApi: android.view.contentcapture.ViewNode#isClickable():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isContextClickable():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isContextClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isEnabled():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isEnabled()
-UnflaggedApi: android.view.contentcapture.ViewNode#isFocusable():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isFocusable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isFocused():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isFocused()
-UnflaggedApi: android.view.contentcapture.ViewNode#isLongClickable():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isLongClickable()
-UnflaggedApi: android.view.contentcapture.ViewNode#isOpaque():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isOpaque()
-UnflaggedApi: android.view.contentcapture.ViewNode#isSelected():
-    New API must be flagged with @FlaggedApi: method android.view.contentcapture.ViewNode.isSelected()
-UnflaggedApi: android.view.translation.UiTranslationSpec#equals(Object):
-    New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.equals(Object)
-UnflaggedApi: android.view.translation.UiTranslationSpec#hashCode():
-    New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.hashCode()
-UnflaggedApi: android.view.translation.UiTranslationSpec#toString():
-    New API must be flagged with @FlaggedApi: method android.view.translation.UiTranslationSpec.toString()
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 642813f..40c6fa8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -50,6 +50,7 @@
     field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
     field public static final String SET_GAME_SERVICE = "android.permission.SET_GAME_SERVICE";
     field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
+    field public static final String START_ACTIVITIES_FROM_SDK_SANDBOX = "android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_INPUT_METHOD = "android.permission.TEST_INPUT_METHOD";
@@ -2115,6 +2116,7 @@
 
   public class SharedConnectivityManager {
     method @Nullable public static android.net.wifi.sharedconnectivity.app.SharedConnectivityManager create(@NonNull android.content.Context, @NonNull String, @NonNull String);
+    method @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
     method @Nullable public android.content.ServiceConnection getServiceConnection();
     method public void setService(@Nullable android.os.IInterface);
   }
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 1aaedab..107be8b 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,985 +1,341 @@
 // Baseline format: 1.0
-AcronymName: android.app.NotificationChannel#isImportanceLockedByOEM():
-    Acronyms should not be capitalized in method names: was `isImportanceLockedByOEM`, should this be `isImportanceLockedByOem`?
-AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean):
-    Acronyms should not be capitalized in method names: was `setImportanceLockedByOEM`, should this be `setImportanceLockedByOem`?
+KotlinKeyword: android.app.Notification#when:
+    Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
 
 
-ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
-    Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]`
-ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11:
-    Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]`
-ArrayReturn: android.view.Display#getSupportedWideColorGamut():
-    Method should return Collection<ColorSpace> (or subclass) instead of raw array; was `android.graphics.ColorSpace[]`
-ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
-    Method parameter should be Collection<View> (or subclass) instead of raw array; was `android.view.View[]`
-ArrayReturn: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0:
-    Method parameter should be Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
-
-
-AutoBoxing: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion():
-    Must avoid boxed primitives (`java.lang.Long`)
-
-
-BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
-    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
-BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
-    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
-
-
-ConcreteCollection: android.content.AutofillOptions#disabledActivities:
-    Field type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
-    Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.content.ContentCaptureOptions#ContentCaptureOptions(int, int, int, int, int, android.util.ArraySet<android.content.ComponentName>) parameter #5:
-    Parameter type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.content.ContentCaptureOptions#whitelistedComponents:
-    Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface
-ConcreteCollection: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
-    Field type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms():
-    Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationArgs():
-    Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-ConcreteCollection: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>) parameter #2:
-    Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms():
-    Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface
-
-
-ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
-    Context is distinct, so it must be the first argument (method `get`)
-
-
-EndsWithImpl: android.view.contentcapture.ViewNode.ViewStructureImpl:
-    Don't expose your implementation details: `ViewStructureImpl` ends with `Impl`
-
-
-Enum: android.view.inspector.InspectableProperty.ValueType:
-    Enums are discouraged in Android APIs
-
-
-EqualsAndHashCode: android.os.StrictMode.ViolationInfo#hashCode():
-    Must override both equals and hashCode; missing one in android.os.StrictMode.ViolationInfo
-
-
-ExecutorRegistration: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener):
-    Registration methods should have overload that accepts delivery Executor: `setParameterListener`
-ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
-    Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
-ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
-    Registration methods should have overload that accepts delivery Executor: `getAppPermissions`
-ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
-    Registration methods should have overload that accepts delivery Executor: `setCallback`
-ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
-    Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
-
-
-ForbiddenSuperClass: android.app.AppDetailsActivity:
-    AppDetailsActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
-
-
-GenericException: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
-    Methods must not throw generic exceptions (`java.lang.Exception`)
-GenericException: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
-    Methods must not throw generic exceptions (`java.lang.Exception`)
-GenericException: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
-    Methods must not throw generic exceptions (`java.lang.Exception`)
-
-
-GetterSetterNames: android.net.NetworkPolicyManager#getRestrictBackground():
-    Symmetric method for `setRestrictBackground` must be named `isRestrictBackground`; was `getRestrictBackground`
-
-
-IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
-    Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
-
-
-IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
-    Intent action constant name must be ACTION_FOO: VOICE_INTERACTION_SERVICE
-IntentName: android.provider.Telephony.Sms.Intents#SMS_CARRIER_PROVISION_ACTION:
-    Intent action constant name must be ACTION_FOO: SMS_CARRIER_PROVISION_ACTION
-
-
-KotlinOperator: android.os.PackageTagsList#contains(android.os.PackageTagsList):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: android.util.SparseArrayMap#get(int, K):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
-ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
-    Listeners should always be at end of argument list (method `countPermissionApps`)
-ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
-    Listeners should always be at end of argument list (method `getAppPermissions`)
-
-
-ManagerConstructor: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context):
-    Managers must always be obtained from Context; no direct constructors
-
-
-MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID:
-    If min/max could change in future, make them dynamic methods: android.os.UserHandle#MIN_SECONDARY_USER_ID
-MinMaxConstant: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS:
-    If min/max could change in future, make them dynamic methods: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS
-
-
-MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
-    android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
-MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
-    android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
-MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
-    android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
-    android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
-    android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
-    android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
-MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
-    android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
-MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int):
-    android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int)
-
-
-MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
-    Missing nullability on parameter `config` in method `onMovedToDisplay`
-MissingNullability: android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName) parameter #0:
-    Missing nullability on parameter `activity` in method `alwaysShowUnsupportedCompileSdkWarning`
-MissingNullability: android.app.ActivityManager#holdLock(android.os.IBinder, int) parameter #0:
-    Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.app.ActivityManager#scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int) parameter #0:
-    Missing nullability on parameter `packages` in method `scheduleApplicationInfoChanged`
-MissingNullability: android.app.ActivityManager.TaskDescription#getIconFilename():
-    Missing nullability on method `getIconFilename` return
-MissingNullability: android.app.ActivityTaskManager#clearLaunchParamsForPackages(java.util.List<java.lang.String>) parameter #0:
-    Missing nullability on parameter `packageNames` in method `clearLaunchParamsForPackages`
-MissingNullability: android.app.ActivityTaskManager#resizeTask(int, android.graphics.Rect) parameter #1:
-    Missing nullability on parameter `bounds` in method `resizeTask`
-MissingNullability: android.app.ActivityTaskManager#supportsMultiWindow(android.content.Context) parameter #0:
-    Missing nullability on parameter `context` in method `supportsMultiWindow`
-MissingNullability: android.app.ActivityTaskManager#supportsSplitScreenMultiWindow(android.content.Context) parameter #0:
-    Missing nullability on parameter `context` in method `supportsSplitScreenMultiWindow`
 MissingNullability: android.app.AppDetailsActivity#onCreate(android.os.Bundle) parameter #0:
     Missing nullability on parameter `savedInstanceState` in method `onCreate`
-MissingNullability: android.app.AppOpsManager#isOperationActive(int, int, String) parameter #2:
-    Missing nullability on parameter `packageName` in method `isOperationActive`
-MissingNullability: android.app.AppOpsManager#opToPermission(int):
-    Missing nullability on method `opToPermission` return
-MissingNullability: android.app.AppOpsManager#permissionToOpCode(String) parameter #0:
-    Missing nullability on parameter `permission` in method `permissionToOpCode`
-MissingNullability: android.app.AppOpsManager#setMode(int, int, String, int) parameter #2:
-    Missing nullability on parameter `packageName` in method `setMode`
-MissingNullability: android.app.NotificationManager#allowAssistantAdjustment(String) parameter #0:
-    Missing nullability on parameter `capability` in method `allowAssistantAdjustment`
-MissingNullability: android.app.NotificationManager#disallowAssistantAdjustment(String) parameter #0:
-    Missing nullability on parameter `capability` in method `disallowAssistantAdjustment`
-MissingNullability: android.app.NotificationManager#getEffectsSuppressor():
-    Missing nullability on method `getEffectsSuppressor` return
-MissingNullability: android.app.TimePickerDialog#getTimePicker():
-    Missing nullability on method `getTimePicker` return
-MissingNullability: android.app.WindowConfiguration#compareTo(android.app.WindowConfiguration) parameter #0:
-    Missing nullability on parameter `that` in method `compareTo`
-MissingNullability: android.app.WindowConfiguration#getAppBounds():
-    Missing nullability on method `getAppBounds` return
-MissingNullability: android.app.WindowConfiguration#getBounds():
-    Missing nullability on method `getBounds` return
-MissingNullability: android.app.WindowConfiguration#setAppBounds(android.graphics.Rect) parameter #0:
-    Missing nullability on parameter `rect` in method `setAppBounds`
-MissingNullability: android.app.WindowConfiguration#setBounds(android.graphics.Rect) parameter #0:
-    Missing nullability on parameter `rect` in method `setBounds`
-MissingNullability: android.app.WindowConfiguration#setTo(android.app.WindowConfiguration) parameter #0:
-    Missing nullability on parameter `other` in method `setTo`
-MissingNullability: android.app.WindowConfiguration#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
-    Missing nullability on method `getOwnerInstalledCaCerts` return
-MissingNullability: android.app.admin.SecurityLog.SecurityEvent#SecurityEvent(long, byte[]) parameter #1:
-    Missing nullability on parameter `data` in method `SecurityEvent`
-MissingNullability: android.app.prediction.AppPredictor#getSessionId():
-    Missing nullability on method `getSessionId` return
-MissingNullability: android.content.AutofillOptions#forWhitelistingItself():
-    Missing nullability on method `forWhitelistingItself` return
-MissingNullability: android.content.AutofillOptions#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.content.ContentCaptureOptions#forWhitelistingItself():
-    Missing nullability on method `forWhitelistingItself` return
-MissingNullability: android.content.ContentCaptureOptions#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int):
-    Missing nullability on method `getSyncAdapterPackagesForAuthorityAsUser` return
-MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int) parameter #0:
-    Missing nullability on parameter `authority` in method `getSyncAdapterPackagesForAuthorityAsUser`
-MissingNullability: android.content.pm.ActivityInfo#isTranslucentOrFloating(android.content.res.TypedArray) parameter #0:
-    Missing nullability on parameter `attributes` in method `isTranslucentOrFloating`
-MissingNullability: android.content.pm.LauncherApps#LauncherApps(android.content.Context) parameter #0:
-    Missing nullability on parameter `context` in method `LauncherApps`
-MissingNullability: android.content.pm.PackageManager#getHoldLockToken():
-    Missing nullability on method `getHoldLockToken` return
-MissingNullability: android.content.pm.PackageManager#getNamesForUids(int[]) parameter #0:
-    Missing nullability on parameter `uids` in method `getNamesForUids`
-MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinder, int) parameter #0:
-    Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0:
-    Missing nullability on parameter `context` in method `ShortcutManager`
-MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0:
-    Missing nullability on parameter `orig` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1:
-    Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2:
-    Missing nullability on parameter `iconPath` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1:
-    Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2:
-    Missing nullability on parameter `iconPath` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4:
-    Missing nullability on parameter `userType` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1:
-    Missing nullability on parameter `name` in method `UserInfo`
-MissingNullability: android.content.pm.UserInfo#getUserHandle():
-    Missing nullability on method `getUserHandle` return
-MissingNullability: android.content.pm.UserInfo#iconPath:
-    Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint:
-    Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#name:
-    Missing nullability on field `name` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#userType:
-    Missing nullability on field `userType` in class `class android.content.pm.UserInfo`
-MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0:
-    Missing nullability on parameter `packageName` in method `getOverlayablesToString`
-MissingNullability: android.content.res.Configuration#windowConfiguration:
-    Missing nullability on field `windowConfiguration` in class `class android.content.res.Configuration`
-MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #0:
-    Missing nullability on parameter `printer` in method `dump`
-MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #1:
-    Missing nullability on parameter `args` in method `dump`
-MissingNullability: android.database.sqlite.SQLiteDebug#getDatabaseInfo():
-    Missing nullability on method `getDatabaseInfo` return
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#DbStats(String, long, long, int, int, int, int) parameter #0:
-    Missing nullability on parameter `dbName` in method `DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#cache:
-    Missing nullability on field `cache` in class `class android.database.sqlite.SQLiteDebug.DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#dbName:
-    Missing nullability on field `dbName` in class `class android.database.sqlite.SQLiteDebug.DbStats`
-MissingNullability: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
-    Missing nullability on field `dbStats` in class `class android.database.sqlite.SQLiteDebug.PagerStats`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #0:
-    Missing nullability on parameter `db` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #1:
-    Missing nullability on parameter `sql` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #2:
-    Missing nullability on parameter `editTable` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #3:
-    Missing nullability on parameter `cancellationSignal` in method `SQLiteDirectCursorDriver`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#cursorRequeried(android.database.Cursor) parameter #0:
-    Missing nullability on parameter `cursor` in method `cursorRequeried`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
-    Missing nullability on method `query` return
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #0:
-    Missing nullability on parameter `factory` in method `query`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #1:
-    Missing nullability on parameter `selectionArgs` in method `query`
-MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#setBindArguments(String[]) parameter #0:
-    Missing nullability on parameter `bindArgs` in method `setBindArguments`
-MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultJournalMode():
-    Missing nullability on method `getDefaultJournalMode` return
-MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultSyncMode():
-    Missing nullability on method `getDefaultSyncMode` return
-MissingNullability: android.database.sqlite.SQLiteGlobal#getWALSyncMode():
-    Missing nullability on method `getWALSyncMode` return
-MissingNullability: android.graphics.ImageDecoder#createSource(android.content.res.Resources, java.io.InputStream, int) parameter #0:
-    Missing nullability on parameter `res` in method `createSource`
-MissingNullability: android.graphics.drawable.AdaptiveIconDrawable#getSafeZone():
-    Missing nullability on method `getSafeZone` return
-MissingNullability: android.graphics.drawable.ColorDrawable#getXfermode():
-    Missing nullability on method `getXfermode` return
-MissingNullability: android.hardware.camera2.CameraManager#getCameraIdListNoLazy():
-    Missing nullability on method `getCameraIdListNoLazy` return
-MissingNullability: android.hardware.display.AmbientDisplayConfiguration#AmbientDisplayConfiguration(android.content.Context) parameter #0:
-    Missing nullability on parameter `context` in method `AmbientDisplayConfiguration`
-MissingNullability: android.media.AudioAttributes#getSdkUsages():
-    Missing nullability on method `getSdkUsages` return
-MissingNullability: android.media.AudioManager#getPublicStreamTypes():
-    Missing nullability on method `getPublicStreamTypes` return
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
-    Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
-    Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #6:
-    Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
-    Missing nullability on parameter `clientEffects` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11:
-    Missing nullability on parameter `deviceEffects` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #3:
-    Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #4:
-    Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
-    Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration`
-MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
-    Missing nullability on method `setAudioStretchMode` return
-MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
-    Missing nullability on field `EFFECT_TYPE_NULL` in class `class android.media.audiofx.AudioEffect`
-MissingNullability: android.media.audiofx.AudioEffect#byteArrayToInt(byte[]) parameter #0:
-    Missing nullability on parameter `valueBuf` in method `byteArrayToInt`
-MissingNullability: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]) parameter #0:
-    Missing nullability on parameter `valueBuf` in method `byteArrayToShort`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #0:
-    Missing nullability on parameter `param` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #1:
-    Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, byte[]) parameter #1:
-    Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, int[]) parameter #1:
-    Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, short[]) parameter #1:
-    Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #0:
-    Missing nullability on parameter `param` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #1:
-    Missing nullability on parameter `value` in method `getParameter`
-MissingNullability: android.media.audiofx.AudioEffect#intToByteArray(int):
-    Missing nullability on method `intToByteArray` return
-MissingNullability: android.media.audiofx.AudioEffect#isEffectTypeAvailable(java.util.UUID) parameter #0:
-    Missing nullability on parameter `type` in method `isEffectTypeAvailable`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #0:
-    Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #1:
-    Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int, byte[]) parameter #1:
-    Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #0:
-    Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #1:
-    Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #0:
-    Missing nullability on parameter `param` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #1:
-    Missing nullability on parameter `value` in method `setParameter`
-MissingNullability: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener) parameter #0:
-    Missing nullability on parameter `listener` in method `setParameterListener`
-MissingNullability: android.media.audiofx.AudioEffect#shortToByteArray(short):
-    Missing nullability on method `shortToByteArray` return
-MissingNullability: android.media.audiofx.AudioEffect.Descriptor#Descriptor(android.os.Parcel) parameter #0:
-    Missing nullability on parameter `in` in method `Descriptor`
-MissingNullability: android.media.audiofx.AudioEffect.Descriptor#writeToParcel(android.os.Parcel) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #0:
-    Missing nullability on parameter `effect` in method `onParameterChange`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #2:
-    Missing nullability on parameter `param` in method `onParameterChange`
-MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #3:
-    Missing nullability on parameter `value` in method `onParameterChange`
-MissingNullability: android.os.Build#is64BitAbi(String) parameter #0:
-    Missing nullability on parameter `abi` in method `is64BitAbi`
-MissingNullability: android.os.Build.VERSION#ACTIVE_CODENAMES:
-    Missing nullability on field `ACTIVE_CODENAMES` in class `class android.os.Build.VERSION`
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...):
-    Missing nullability on method `buildPath` return
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #0:
-    Missing nullability on parameter `base` in method `buildPath`
-MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #1:
-    Missing nullability on parameter `segments` in method `buildPath`
-MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #0:
-    Missing nullability on parameter `dir` in method `contains`
-MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #1:
-    Missing nullability on parameter `file` in method `contains`
-MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor):
-    Missing nullability on method `getFile` return
-MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor) parameter #0:
-    Missing nullability on parameter `fd` in method `getFile`
-MissingNullability: android.os.StrictMode#setViolationLogger(android.os.StrictMode.ViolationLogger) parameter #0:
-    Missing nullability on parameter `listener` in method `setViolationLogger`
-MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel) parameter #0:
-    Missing nullability on parameter `in` in method `ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel, boolean) parameter #0:
-    Missing nullability on parameter `in` in method `ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#broadcastIntentAction:
-    Missing nullability on field `broadcastIntentAction` in class `class android.os.StrictMode.ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #0:
-    Missing nullability on parameter `pw` in method `dump`
-MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #1:
-    Missing nullability on parameter `prefix` in method `dump`
-MissingNullability: android.os.StrictMode.ViolationInfo#getStackTrace():
-    Missing nullability on method `getStackTrace` return
-MissingNullability: android.os.StrictMode.ViolationInfo#getViolationClass():
-    Missing nullability on method `getViolationClass` return
-MissingNullability: android.os.StrictMode.ViolationInfo#getViolationDetails():
-    Missing nullability on method `getViolationDetails` return
-MissingNullability: android.os.StrictMode.ViolationInfo#tags:
-    Missing nullability on field `tags` in class `class android.os.StrictMode.ViolationInfo`
-MissingNullability: android.os.StrictMode.ViolationInfo#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
-MissingNullability: android.os.StrictMode.ViolationLogger#log(android.os.StrictMode.ViolationInfo) parameter #0:
-    Missing nullability on parameter `info` in method `log`
-MissingNullability: android.os.VibrationEffect#RINGTONES:
-    Missing nullability on field `RINGTONES` in class `class android.os.VibrationEffect`
-MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #0:
-    Missing nullability on parameter `uri` in method `get`
-MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
-    Missing nullability on parameter `context` in method `get`
-MissingNullability: android.os.VibrationEffect#get(int):
-    Missing nullability on method `get` return
-MissingNullability: android.os.VibrationEffect#get(int, boolean):
-    Missing nullability on method `get` return
-MissingNullability: android.os.VintfObject#getHalNamesAndVersions():
-    Missing nullability on method `getHalNamesAndVersions` return
-MissingNullability: android.os.VintfObject#getSepolicyVersion():
-    Missing nullability on method `getSepolicyVersion` return
-MissingNullability: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion():
-    Missing nullability on method `getTargetFrameworkCompatibilityMatrixVersion` return
-MissingNullability: android.os.VintfObject#getVndkSnapshots():
-    Missing nullability on method `getVndkSnapshots` return
-MissingNullability: android.os.VintfObject#report():
-    Missing nullability on method `report` return
-MissingNullability: android.os.VintfRuntimeInfo#getCpuInfo():
-    Missing nullability on method `getCpuInfo` return
-MissingNullability: android.os.VintfRuntimeInfo#getHardwareId():
-    Missing nullability on method `getHardwareId` return
-MissingNullability: android.os.VintfRuntimeInfo#getKernelVersion():
-    Missing nullability on method `getKernelVersion` return
-MissingNullability: android.os.VintfRuntimeInfo#getNodeName():
-    Missing nullability on method `getNodeName` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsName():
-    Missing nullability on method `getOsName` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsRelease():
-    Missing nullability on method `getOsRelease` return
-MissingNullability: android.os.VintfRuntimeInfo#getOsVersion():
-    Missing nullability on method `getOsVersion` return
-MissingNullability: android.os.WorkSource#add(int, String) parameter #1:
-    Missing nullability on parameter `name` in method `add`
-MissingNullability: android.os.health.HealthKeys.Constants#Constants(Class) parameter #0:
-    Missing nullability on parameter `clazz` in method `Constants`
-MissingNullability: android.os.health.HealthKeys.Constants#getDataType():
-    Missing nullability on method `getDataType` return
-MissingNullability: android.os.health.HealthKeys.Constants#getKeys(int):
-    Missing nullability on method `getKeys` return
-MissingNullability: android.os.health.HealthStats#HealthStats(android.os.Parcel) parameter #0:
-    Missing nullability on parameter `in` in method `HealthStats`
-MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel) parameter #0:
-    Missing nullability on parameter `in` in method `HealthStatsParceler`
-MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.health.HealthStatsWriter) parameter #0:
-    Missing nullability on parameter `writer` in method `HealthStatsParceler`
-MissingNullability: android.os.health.HealthStatsParceler#getHealthStats():
-    Missing nullability on method `getHealthStats` return
-MissingNullability: android.os.health.HealthStatsParceler#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
-MissingNullability: android.os.health.HealthStatsWriter#HealthStatsWriter(android.os.health.HealthKeys.Constants) parameter #0:
-    Missing nullability on parameter `constants` in method `HealthStatsWriter`
-MissingNullability: android.os.health.HealthStatsWriter#addMeasurements(int, String, long) parameter #1:
-    Missing nullability on parameter `name` in method `addMeasurements`
-MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #1:
-    Missing nullability on parameter `name` in method `addStats`
-MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #2:
-    Missing nullability on parameter `value` in method `addStats`
-MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #1:
-    Missing nullability on parameter `name` in method `addTimers`
-MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #2:
-    Missing nullability on parameter `value` in method `addTimers`
-MissingNullability: android.os.health.HealthStatsWriter#flattenToParcel(android.os.Parcel) parameter #0:
-    Missing nullability on parameter `out` in method `flattenToParcel`
-MissingNullability: android.os.storage.StorageVolume#getPath():
-    Missing nullability on method `getPath` return
-MissingNullability: android.provider.CalendarContract.Calendars#SYNC_WRITABLE_COLUMNS:
-    Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Calendars`
-MissingNullability: android.provider.CalendarContract.Events#SYNC_WRITABLE_COLUMNS:
-    Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Events`
-MissingNullability: android.provider.ContactsContract.RawContactsEntity#CORP_CONTENT_URI:
-    Missing nullability on field `CORP_CONTENT_URI` in class `class android.provider.ContactsContract.RawContactsEntity`
-MissingNullability: android.security.keystore.KeyProtection.Builder#setBoundToSpecificSecureUserId(long):
-    Missing nullability on method `setBoundToSpecificSecureUserId` return
-MissingNullability: android.service.autofill.CompositeUserData#getCategoryIds():
-    Missing nullability on method `getCategoryIds` return
-MissingNullability: android.service.autofill.CompositeUserData#getDefaultFieldClassificationArgs():
-    Missing nullability on method `getDefaultFieldClassificationArgs` return
-MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms():
-    Missing nullability on method `getFieldClassificationAlgorithms` return
-MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationArgs():
-    Missing nullability on method `getFieldClassificationArgs` return
-MissingNullability: android.service.autofill.CompositeUserData#getValues():
-    Missing nullability on method `getValues` return
-MissingNullability: android.service.autofill.CompositeUserData#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `parcel` in method `writeToParcel`
-MissingNullability: android.service.autofill.UserData#getFieldClassificationAlgorithms():
-    Missing nullability on method `getFieldClassificationAlgorithms` return
-MissingNullability: android.telecom.Call.Details#getTelecomCallId():
-    Missing nullability on method `getTelecomCallId` return
-MissingNullability: android.telephony.ServiceState#addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo) parameter #0:
-    Missing nullability on parameter `nri` in method `addNetworkRegistrationInfo`
-MissingNullability: android.telephony.ServiceState#setCellBandwidths(int[]) parameter #0:
-    Missing nullability on parameter `bandwidths` in method `setCellBandwidths`
-MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #0:
-    Missing nullability on parameter `destAddress` in method `checkSmsShortCodeDestination`
-MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #1:
-    Missing nullability on parameter `countryIso` in method `checkSmsShortCodeDestination`
-MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNKNOWN:
-    Missing nullability on field `HAL_VERSION_UNKNOWN` in class `class android.telephony.TelephonyManager`
-MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNSUPPORTED:
-    Missing nullability on field `HAL_VERSION_UNSUPPORTED` in class `class android.telephony.TelephonyManager`
-MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag():
-    Missing nullability on method `getLine1AlphaTag` return
-MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion():
-    Missing nullability on method `getRadioHalVersion` return
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #0:
-    Missing nullability on parameter `mccmnc` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #1:
-    Missing nullability on parameter `imsi` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #2:
-    Missing nullability on parameter `iccid` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #3:
-    Missing nullability on parameter `gid1` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #4:
-    Missing nullability on parameter `gid2` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #5:
-    Missing nullability on parameter `plmn` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #6:
-    Missing nullability on parameter `spn` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #7:
-    Missing nullability on parameter `carrierPriviledgeRules` in method `setCarrierTestOverride`
-MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8:
-    Missing nullability on parameter `apn` in method `setCarrierTestOverride`
-MissingNullability: android.text.Selection.MemoryTextWatcher#afterTextChanged(android.text.Editable) parameter #0:
-    Missing nullability on parameter `s` in method `afterTextChanged`
-MissingNullability: android.text.Selection.MemoryTextWatcher#beforeTextChanged(CharSequence, int, int, int) parameter #0:
-    Missing nullability on parameter `s` in method `beforeTextChanged`
-MissingNullability: android.text.Selection.MemoryTextWatcher#onTextChanged(CharSequence, int, int, int) parameter #0:
-    Missing nullability on parameter `s` in method `onTextChanged`
-MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene):
-    Missing nullability on method `getTransition` return
-MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene) parameter #0:
-    Missing nullability on parameter `scene` in method `getTransition`
-MissingNullability: android.util.FeatureFlagUtils#getAllFeatureFlags():
-    Missing nullability on method `getAllFeatureFlags` return
-MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #0:
-    Missing nullability on parameter `context` in method `isEnabled`
-MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #1:
-    Missing nullability on parameter `feature` in method `isEnabled`
-MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #0:
-    Missing nullability on parameter `context` in method `setEnabled`
-MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #1:
-    Missing nullability on parameter `feature` in method `setEnabled`
-MissingNullability: android.util.TimeUtils#formatDuration(long):
-    Missing nullability on method `formatDuration` return
-MissingNullability: android.util.proto.EncodedBuffer#dumpBuffers(String) parameter #0:
-    Missing nullability on parameter `tag` in method `dumpBuffers`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #0:
-    Missing nullability on parameter `tag` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #1:
-    Missing nullability on parameter `prefix` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #2:
-    Missing nullability on parameter `buf` in method `dumpByteString`
-MissingNullability: android.util.proto.EncodedBuffer#getBytes(int):
-    Missing nullability on method `getBytes` return
-MissingNullability: android.util.proto.EncodedBuffer#getDebugString():
-    Missing nullability on method `getDebugString` return
-MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[]) parameter #0:
-    Missing nullability on parameter `val` in method `writeRawBuffer`
-MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[], int, int) parameter #0:
-    Missing nullability on parameter `val` in method `writeRawBuffer`
-MissingNullability: android.util.proto.ProtoParseException#ProtoParseException(String) parameter #0:
-    Missing nullability on parameter `msg` in method `ProtoParseException`
-MissingNullability: android.util.proto.WireTypeMismatchException#WireTypeMismatchException(String) parameter #0:
-    Missing nullability on parameter `msg` in method `WireTypeMismatchException`
-MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #1:
-    Missing nullability on parameter `action` in method `postCallback`
-MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #2:
-    Missing nullability on parameter `token` in method `postCallback`
-MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #1:
-    Missing nullability on parameter `action` in method `postCallbackDelayed`
-MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #2:
-    Missing nullability on parameter `token` in method `postCallbackDelayed`
-MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #1:
-    Missing nullability on parameter `action` in method `removeCallbacks`
-MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #2:
-    Missing nullability on parameter `token` in method `removeCallbacks`
-MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
-    Missing nullability on parameter `views` in method `sort`
-MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #3:
-    Missing nullability on parameter `root` in method `sort`
-MissingNullability: android.view.KeyEvent#actionToString(int):
-    Missing nullability on method `actionToString` return
-MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
-    Missing nullability on parameter `attrs` in method `relayout`
-MissingNullability: android.view.View#getTooltipView():
-    Missing nullability on method `getTooltipView` return
-MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
-    Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1:
-    Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #0:
-    Missing nullability on parameter `tree` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #1:
-    Missing nullability on parameter `executor` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #2:
-    Missing nullability on parameter `callback` in method `startRenderingCommandsCapture`
-MissingNullability: android.view.WindowManager#holdLock(android.os.IBinder, int) parameter #0:
-    Missing nullability on parameter `token` in method `holdLock`
-MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle:
-    Missing nullability on field `accessibilityTitle` in class `class android.view.WindowManager.LayoutParams`
-MissingNullability: android.view.accessibility.AccessibilityNodeInfo#writeToParcelNoRecycle(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `parcel` in method `writeToParcelNoRecycle`
-MissingNullability: android.view.accessibility.AccessibilityWindowInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0:
-    Missing nullability on parameter `counter` in method `setNumInstancesInUseCounter`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#asyncNewChild(int):
-    Missing nullability on method `asyncNewChild` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getAutofillId():
-    Missing nullability on method `getAutofillId` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getExtras():
-    Missing nullability on method `getExtras` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getHint():
-    Missing nullability on method `getHint` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getNode():
-    Missing nullability on method `getNode` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getTempRect():
-    Missing nullability on method `getTempRect` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getText():
-    Missing nullability on method `getText` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newChild(int):
-    Missing nullability on method `newChild` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String):
-    Missing nullability on method `newHtmlInfoBuilder` return
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String) parameter #0:
-    Missing nullability on parameter `tagName` in method `newHtmlInfoBuilder`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillHints(String[]) parameter #0:
-    Missing nullability on parameter `hints` in method `setAutofillHints`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId) parameter #0:
-    Missing nullability on parameter `id` in method `setAutofillId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId, int) parameter #0:
-    Missing nullability on parameter `parentId` in method `setAutofillId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0:
-    Missing nullability on parameter `options` in method `setAutofillOptions`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillValue(android.view.autofill.AutofillValue) parameter #0:
-    Missing nullability on parameter `value` in method `setAutofillValue`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setClassName(String) parameter #0:
-    Missing nullability on parameter `className` in method `setClassName`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setContentDescription(CharSequence) parameter #0:
-    Missing nullability on parameter `contentDescription` in method `setContentDescription`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHint(CharSequence) parameter #0:
-    Missing nullability on parameter `hint` in method `setHint`
 MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHintIdEntry(String) parameter #0:
     Missing nullability on parameter `entryName` in method `setHintIdEntry`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHtmlInfo(android.view.ViewStructure.HtmlInfo) parameter #0:
-    Missing nullability on parameter `htmlInfo` in method `setHtmlInfo`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #1:
-    Missing nullability on parameter `packageName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #2:
-    Missing nullability on parameter `typeName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #3:
-    Missing nullability on parameter `entryName` in method `setId`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setLocaleList(android.os.LocaleList) parameter #0:
-    Missing nullability on parameter `localeList` in method `setLocaleList`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence) parameter #0:
-    Missing nullability on parameter `text` in method `setText`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence, int, int) parameter #0:
-    Missing nullability on parameter `text` in method `setText`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #0:
-    Missing nullability on parameter `charOffsets` in method `setTextLines`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #1:
-    Missing nullability on parameter `baselines` in method `setTextLines`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTransformation(android.graphics.Matrix) parameter #0:
-    Missing nullability on parameter `matrix` in method `setTransformation`
-MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setWebDomain(String) parameter #0:
-    Missing nullability on parameter `domain` in method `setWebDomain`
-MissingNullability: android.widget.CalendarView#getBoundsForDate(long, android.graphics.Rect) parameter #1:
-    Missing nullability on parameter `outBounds` in method `getBoundsForDate`
 MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
     Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded`
 MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1:
     Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
-MissingNullability: android.widget.Magnifier#getMagnifierDefaultSize():
-    Missing nullability on method `getMagnifierDefaultSize` return
-MissingNullability: android.widget.Magnifier#setOnOperationCompleteCallback(android.widget.Magnifier.Callback) parameter #0:
-    Missing nullability on parameter `callback` in method `setOnOperationCompleteCallback`
-MissingNullability: android.widget.NumberPicker#getDisplayedValueForCurrentSelection():
-    Missing nullability on method `getDisplayedValueForCurrentSelection` return
-MissingNullability: android.widget.PopupMenu#getMenuListView():
-    Missing nullability on method `getMenuListView` return
-MissingNullability: android.widget.TimePicker#getAmView():
-    Missing nullability on method `getAmView` return
-MissingNullability: android.widget.TimePicker#getHourView():
-    Missing nullability on method `getHourView` return
-MissingNullability: android.widget.TimePicker#getMinuteView():
-    Missing nullability on method `getMinuteView` return
-MissingNullability: android.widget.TimePicker#getPmView():
-    Missing nullability on method `getPmView` return
-
-
-MutableBareField: android.content.AutofillOptions#appDisabledExpiration:
-    Bare field appDisabledExpiration must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#augmentedAutofillEnabled:
-    Bare field augmentedAutofillEnabled must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#disabledActivities:
-    Bare field disabledActivities must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
-    Bare field whitelistedActivitiesForAugmentedAutofill must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated:
-    Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#creationTime:
-    Bare field creationTime must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#flags:
-    Bare field flags must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#guestToRemove:
-    Bare field guestToRemove must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#iconPath:
-    Bare field iconPath must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#id:
-    Bare field id must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint:
-    Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#lastLoggedInTime:
-    Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#name:
-    Bare field name must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#partial:
-    Bare field partial must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#preCreated:
-    Bare field preCreated must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#profileBadge:
-    Bare field profileBadge must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#profileGroupId:
-    Bare field profileGroupId must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId:
-    Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#serialNumber:
-    Bare field serialNumber must be marked final, or moved behind accessors if mutable
-MutableBareField: android.content.pm.UserInfo#userType:
-    Bare field userType must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache:
-    Bare field cache must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName:
-    Bare field dbName must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbSize:
-    Bare field dbSize must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#lookaside:
-    Bare field lookaside must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#pageSize:
-    Bare field pageSize must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#dbStats:
-    Bare field dbStats must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#largestMemAlloc:
-    Bare field largestMemAlloc must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#memoryUsed:
-    Bare field memoryUsed must be marked final, or moved behind accessors if mutable
-MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#pageCacheOverflow:
-    Bare field pageCacheOverflow must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#broadcastIntentAction:
-    Bare field broadcastIntentAction must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#durationMillis:
-    Bare field durationMillis must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#numAnimationsRunning:
-    Bare field numAnimationsRunning must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#numInstances:
-    Bare field numInstances must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#tags:
-    Bare field tags must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#violationNumThisLoop:
-    Bare field violationNumThisLoop must be marked final, or moved behind accessors if mutable
-MutableBareField: android.os.StrictMode.ViolationInfo#violationUptimeMillis:
-    Bare field violationUptimeMillis must be marked final, or moved behind accessors if mutable
-
-
-NoByteOrShort: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]):
-    Should avoid odd sized primitives; use `int` instead of `short` in method android.media.audiofx.AudioEffect.byteArrayToShort(byte[])
-NoByteOrShort: android.media.audiofx.AudioEffect#setParameter(int, short) parameter #1:
-    Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.setParameter(int param, short value)
-NoByteOrShort: android.media.audiofx.AudioEffect#shortToByteArray(short) parameter #0:
-    Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.shortToByteArray(short value)
-NoByteOrShort: android.util.proto.EncodedBuffer#readRawByte():
-    Should avoid odd sized primitives; use `int` instead of `byte` in method android.util.proto.EncodedBuffer.readRawByte()
-NoByteOrShort: android.util.proto.EncodedBuffer#writeRawByte(byte) parameter #0:
-    Should avoid odd sized primitives; use `int` instead of `byte` in parameter val in android.util.proto.EncodedBuffer.writeRawByte(byte val)
-
-
-NoSettingsProvider: android.provider.Settings.Global#APP_OPS_CONSTANTS:
-    New setting keys are not allowed (Field: APP_OPS_CONSTANTS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
-    New setting keys are not allowed (Field: AUTOMATIC_POWER_SAVE_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#BATTERY_SAVER_CONSTANTS:
-    New setting keys are not allowed (Field: BATTERY_SAVER_CONSTANTS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD:
-    New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABLED:
-    New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HDR_CONVERSION_MODE:
-    New setting keys are not allowed (Field: HDR_CONVERSION_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HDR_FORCE_CONVERSION_TYPE:
-    New setting keys are not allowed (Field: HDR_FORCE_CONVERSION_TYPE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
-    New setting keys are not allowed (Field: HIDDEN_API_BLACKLIST_EXEMPTIONS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY:
-    New setting keys are not allowed (Field: HIDDEN_API_POLICY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
-    New setting keys are not allowed (Field: HIDE_ERROR_DIALOGS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE:
-    New setting keys are not allowed (Field: LOW_POWER_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE_STICKY:
-    New setting keys are not allowed (Field: LOW_POWER_MODE_STICKY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES:
-    New setting keys are not allowed (Field: OVERLAY_DISPLAY_DEVICES); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
-    New setting keys are not allowed (Field: ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
-    New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_CAPABILITY); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
-    New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_MODE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
-    New setting keys are not allowed (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE:
-    New setting keys are not allowed (Field: AUTOFILL_SERVICE); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#BIOMETRIC_VIRTUAL_ENABLED:
-    New setting keys are not allowed (Field: BIOMETRIC_VIRTUAL_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED:
-    New setting keys are not allowed (Field: CONTENT_CAPTURE_ENABLED); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES:
-    New setting keys are not allowed (Field: DISABLED_PRINT_SERVICES); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
-    New setting keys are not allowed (Field: ENABLED_VR_LISTENERS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#IMMERSIVE_MODE_CONFIRMATIONS:
-    New setting keys are not allowed (Field: IMMERSIVE_MODE_CONFIRMATIONS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
-    New setting keys are not allowed (Field: NOTIFICATION_BADGING); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#POWER_MENU_LOCKED_SHOW_CONTENT:
-    New setting keys are not allowed (Field: POWER_MENU_LOCKED_SHOW_CONTENT); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
-    New setting keys are not allowed (Field: SYNC_PARENT_SOUNDS); use getters/setters in relevant manager class
-NoSettingsProvider: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
-    New setting keys are not allowed (Field: VOICE_INTERACTION_SERVICE); use getters/setters in relevant manager class
-
-
-OnNameExpected: android.service.notification.ConditionProviderService#isBound():
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-
-
-PackageLayering: android.util.FeatureFlagUtils:
-    Method parameter type `android.content.Context` violates package layering: nothing in `package android.util` should depend on `package android.content`
-
-
-ParcelConstructor: android.credentials.ui.ProviderData#ProviderData(android.os.Parcel):
-    Parcelable inflation is exposed through CREATOR, not raw constructors, in android.credentials.ui.ProviderData
-ParcelConstructor: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel):
-    Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.StrictMode.ViolationInfo
-ParcelConstructor: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel):
-    Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.health.HealthStatsParceler
-
-
-ParcelCreator: android.app.WindowConfiguration:
-    Parcelable requires a `CREATOR` field; missing in android.app.WindowConfiguration
-ParcelCreator: android.service.autofill.InternalOnClickAction:
-    Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalOnClickAction
-ParcelCreator: android.service.autofill.InternalSanitizer:
-    Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalSanitizer
-ParcelCreator: android.service.autofill.InternalTransformation:
-    Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalTransformation
-ParcelCreator: android.service.autofill.InternalValidator:
-    Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalValidator
-
-
-ParcelNotFinal: android.app.WindowConfiguration:
-    Parcelable classes must be final: android.app.WindowConfiguration is not final
-ParcelNotFinal: android.content.pm.UserInfo:
-    Parcelable classes must be final: android.content.pm.UserInfo is not final
-ParcelNotFinal: android.os.health.HealthStatsParceler:
-    Parcelable classes must be final: android.os.health.HealthStatsParceler is not final
-ParcelNotFinal: android.service.autofill.InternalOnClickAction:
-    Parcelable classes must be final: android.service.autofill.InternalOnClickAction is not final
-ParcelNotFinal: android.service.autofill.InternalSanitizer:
-    Parcelable classes must be final: android.service.autofill.InternalSanitizer is not final
-ParcelNotFinal: android.service.autofill.InternalTransformation:
-    Parcelable classes must be final: android.service.autofill.InternalTransformation is not final
-ParcelNotFinal: android.service.autofill.InternalValidator:
-    Parcelable classes must be final: android.service.autofill.InternalValidator is not final
 
 
 ProtectedMember: android.app.AppDetailsActivity#onCreate(android.os.Bundle):
     Protected methods not allowed; must be public: method android.app.AppDetailsActivity.onCreate(android.os.Bundle)}
-ProtectedMember: android.view.View#resetResolvedDrawables():
-    Protected methods not allowed; must be public: method android.view.View.resetResolvedDrawables()}
 ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
     Protected methods not allowed; must be public: method android.view.ViewGroup.resetResolvedDrawables()}
 
 
-RethrowRemoteException: android.app.ActivityManager#resumeAppSwitches():
-    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+SamShouldBeLast: android.animation.ValueAnimator#ofObject(android.animation.TypeEvaluator, java.lang.Object...):
+    SAM-compatible parameters (such as parameter 1, "evaluator", in android.animation.ValueAnimator.ofObject) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions):
+    SAM-compatible parameters (such as parameter 1, "callback", in android.app.Activity.convertToTranslucent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.app.ActivityManager.addOnUidImportanceListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource):
+    SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.setExact) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.setWindow) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler):
+    SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String):
+    SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String, android.os.Bundle):
+    SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(int, android.app.PendingIntent.OnFinished, android.os.Handler):
+    SAM-compatible parameters (such as parameter 2, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.UiAutomation#executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long):
+    SAM-compatible parameters (such as parameter 2, "filter", in android.app.UiAutomation.executeAndWaitForEvent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ActivityInfo#dump(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ActivityInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ApplicationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpBack(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpFront(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.database.sqlite.SQLiteCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
+    SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.AdaptiveIconDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+    SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.AdaptiveIconDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable#scheduleSelf(Runnable, long):
+    SAM-compatible parameters (such as parameter 1, "what", in android.graphics.drawable.Drawable.scheduleSelf) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable.Callback#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+    SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.Drawable.Callback.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.LayerDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+    SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.LayerDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes):
+    SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.abandonAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int):
+    SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy):
+    SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
+    SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.MediaCas#setEventListener(android.media.MediaCas.EventListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaCas.setEventListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.os.Parcel#createFixedArray(Class<T>, java.util.function.Function<android.os.IBinder,S>, int...):
+    SAM-compatible parameters (such as parameter 2, "asInterface", in android.os.Parcel.createFixedArray) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], String, int, String):
+    SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], android.net.Uri, String):
+    SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Choreographer#postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long):
+    SAM-compatible parameters (such as parameter 1, "callback", in android.view.Choreographer.postFrameCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
+    SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
+    SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postOnAnimationDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+    SAM-compatible parameters (such as parameter 2, "what", in android.view.View.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.view.Window.addOnFrameMetricsAvailableListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 
 
-SamShouldBeLast: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]):
-    SAM-compatible parameters (such as parameter 1, "printer", in android.database.sqlite.SQLiteDebug.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
-    SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteDirectCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
-    SAM-compatible parameters (such as parameter 1, "pw", in android.os.StrictMode.ViolationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
-    SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
-    SAM-compatible parameters (such as parameter 2, "callback", in android.permission.PermissionControllerManager.getAppPermissions) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
-    SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.CharSequenceTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
-    SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.DateTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
-    SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.ImageTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>):
-    SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.InternalTransformation.batchApply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object):
-    SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long):
-    SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-SamShouldBeLast: android.view.Choreographer#removeCallbacks(int, Runnable, Object):
-    SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.removeCallbacks) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-
-
-StaticUtils: android.os.health.HealthKeys:
-    Fully-static utility classes must not have constructor
-StaticUtils: android.service.autofill.InternalTransformation:
-    Fully-static utility classes must not have constructor
-StaticUtils: android.util.FeatureFlagUtils:
-    Fully-static utility classes must not have constructor
-
-
-StreamFiles: android.os.Environment#buildPath(java.io.File, java.lang.String...):
-    Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.Environment.buildPath(java.io.File,java.lang.String...)
-StreamFiles: android.os.FileUtils#contains(java.io.File, java.io.File):
-    Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.FileUtils.contains(java.io.File,java.io.File)
-
-
-UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
-    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
-    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
-    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
-
-
-UserHandle: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.app.usage.StorageStatsManager#queryCratesForPackage(java.util.UUID, String, android.os.UserHandle):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.UUID, android.os.UserHandle):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
-
-
-UserHandleName: android.content.AutofillOptions:
-    Classes holding a set of parameters should be called `FooParams`, was `AutofillOptions`
-UserHandleName: android.content.ContentCaptureOptions:
-    Classes holding a set of parameters should be called `FooParams`, was `ContentCaptureOptions`
-
-
-VisiblySynchronized: PsiThisExpression:
-    Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
-VisiblySynchronized: android.content.res.AssetManager#getApkPaths():
-    Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
-VisiblySynchronized: android.content.res.AssetManager#getLastResourceResolution():
-    Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getLastResourceResolution()
-VisiblySynchronized: android.content.res.AssetManager#getOverlayablesToString(String):
-    Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getOverlayablesToString(String)
-VisiblySynchronized: android.content.res.AssetManager#setResourceResolutionLoggingEnabled(boolean):
-    Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.setResourceResolutionLoggingEnabled(boolean)
-VisiblySynchronized: android.os.MessageQueue#removeSyncBarrier(int):
-    Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.os.MessageQueue.removeSyncBarrier(int)
+UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
+    New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
+UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX:
+    New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX
+UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH:
+    New API must be flagged with @FlaggedApi: field android.Manifest.permission.USE_REMOTE_AUTH
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERMITTED_INPUT_METHODS_POLICY:
+    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERSONAL_APPS_SUSPENDED_POLICY:
+    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERSONAL_APPS_SUSPENDED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#SCREEN_CAPTURE_DISABLED_POLICY:
+    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.SCREEN_CAPTURE_DISABLED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY:
+    New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.USB_DATA_SIGNALING_POLICY
+UnflaggedApi: android.companion.AssociationInfo.Builder:
+    New API must be flagged with @FlaggedApi: class android.companion.AssociationInfo.Builder
+UnflaggedApi: android.companion.AssociationInfo.Builder#Builder(android.companion.AssociationInfo):
+    New API must be flagged with @FlaggedApi: constructor android.companion.AssociationInfo.Builder(android.companion.AssociationInfo)
+UnflaggedApi: android.companion.AssociationInfo.Builder#Builder(int, int, String):
+    New API must be flagged with @FlaggedApi: constructor android.companion.AssociationInfo.Builder(int,int,String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#build():
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.build()
+UnflaggedApi: android.companion.AssociationInfo.Builder#setAssociatedDevice(android.companion.AssociatedDevice):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setAssociatedDevice(android.companion.AssociatedDevice)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDeviceMacAddress(android.net.MacAddress):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDeviceMacAddress(android.net.MacAddress)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDeviceProfile(String):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDeviceProfile(String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setDisplayName(CharSequence):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setDisplayName(CharSequence)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setLastTimeConnected(long):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setLastTimeConnected(long)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setNotifyOnDeviceNearby(boolean):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setNotifyOnDeviceNearby(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setRevoked(boolean):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setRevoked(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setSelfManaged(boolean):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setSelfManaged(boolean)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setSystemDataSyncFlags(int):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setSystemDataSyncFlags(int)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setTag(String):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setTag(String)
+UnflaggedApi: android.companion.AssociationInfo.Builder#setTimeApproved(long):
+    New API must be flagged with @FlaggedApi: method android.companion.AssociationInfo.Builder.setTimeApproved(long)
+UnflaggedApi: android.companion.CompanionDeviceManager#MESSAGE_REQUEST_PING:
+    New API must be flagged with @FlaggedApi: field android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING
+UnflaggedApi: android.companion.CompanionDeviceManager#enableSecureTransport(boolean):
+    New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.enableSecureTransport(boolean)
+UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], android.content.AttributionSource):
+    New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],android.content.AttributionSource)
+UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], int, android.content.AttributionSource):
+    New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],int,android.content.AttributionSource)
+UnflaggedApi: android.content.pm.UserInfo#isCommunalProfile():
+    New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isCommunalProfile()
+UnflaggedApi: android.content.pm.UserInfo#isPrivateProfile():
+    New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isPrivateProfile()
+UnflaggedApi: android.credentials.CredentialProviderInfo#isPrimary():
+    New API must be flagged with @FlaggedApi: method android.credentials.CredentialProviderInfo.isPrimary()
+UnflaggedApi: android.media.AudioManager#enterAudioFocusFreezeForTest(java.util.List<java.lang.Integer>):
+    New API must be flagged with @FlaggedApi: method android.media.AudioManager.enterAudioFocusFreezeForTest(java.util.List<java.lang.Integer>)
+UnflaggedApi: android.media.AudioManager#exitAudioFocusFreezeForTest():
+    New API must be flagged with @FlaggedApi: method android.media.AudioManager.exitAudioFocusFreezeForTest()
+UnflaggedApi: android.media.AudioManager#getFocusDuckedUidsForTest():
+    New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusDuckedUidsForTest()
+UnflaggedApi: android.media.AudioManager#getFocusFadeOutDurationForTest():
+    New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusFadeOutDurationForTest()
+UnflaggedApi: android.media.AudioManager#getFocusUnmuteDelayAfterFadeOutForTest():
+    New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusUnmuteDelayAfterFadeOutForTest()
+UnflaggedApi: android.media.RingtoneSelection:
+    New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection
+UnflaggedApi: android.media.RingtoneSelection#DEFAULT_SELECTION_URI_STRING:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.DEFAULT_SELECTION_URI_STRING
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_ONLY:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_ONLY
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_OR_SOUND:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_OR_SOUND
+UnflaggedApi: android.media.RingtoneSelection#FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.FROM_URI_RINGTONE_SELECTION_OR_VIBRATION
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_AUDIO_CHANNEL
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_HAPTIC_GENERATOR:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.fromUri(android.net.Uri,int)
+UnflaggedApi: android.media.RingtoneSelection#getSoundSource():
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getSoundSource()
+UnflaggedApi: android.media.RingtoneSelection#getSoundUri():
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getSoundUri()
+UnflaggedApi: android.media.RingtoneSelection#getVibrationSource():
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getVibrationSource()
+UnflaggedApi: android.media.RingtoneSelection#getVibrationUri():
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.getVibrationUri()
+UnflaggedApi: android.media.RingtoneSelection#isRingtoneSelectionUri(android.net.Uri):
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.isRingtoneSelectionUri(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection#toUri():
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.toUri()
+UnflaggedApi: android.media.RingtoneSelection.Builder:
+    New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection.Builder
+UnflaggedApi: android.media.RingtoneSelection.Builder#Builder():
+    New API must be flagged with @FlaggedApi: constructor android.media.RingtoneSelection.Builder()
+UnflaggedApi: android.media.RingtoneSelection.Builder#Builder(android.media.RingtoneSelection):
+    New API must be flagged with @FlaggedApi: constructor android.media.RingtoneSelection.Builder(android.media.RingtoneSelection)
+UnflaggedApi: android.media.RingtoneSelection.Builder#build():
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.build()
+UnflaggedApi: android.media.RingtoneSelection.Builder#setSoundSource(android.net.Uri):
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setSoundSource(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setSoundSource(int):
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setSoundSource(int)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setVibrationSource(android.net.Uri):
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setVibrationSource(android.net.Uri)
+UnflaggedApi: android.media.RingtoneSelection.Builder#setVibrationSource(int):
+    New API must be flagged with @FlaggedApi: method android.media.RingtoneSelection.Builder.setVibrationSource(int)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerInstrumentation#setInPhoneCallState(boolean):
+    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerInstrumentation.setInPhoneCallState(boolean)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties):
+    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForTestModule():
+    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForTestModule()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#listModuleProperties():
+    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.listModuleProperties()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel):
+    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager.Model#getSoundModel():
+    New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.Model.getSoundModel()
+UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivityManager#getBroadcastReceiver():
+    New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivityManager.getBroadcastReceiver()
+UnflaggedApi: android.os.BatteryManager#BATTERY_PLUGGED_ANY:
+    New API must be flagged with @FlaggedApi: field android.os.BatteryManager.BATTERY_PLUGGED_ANY
+UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_MAX_VALUE:
+    New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_MAX_VALUE
+UnflaggedApi: android.os.PowerManager#isBatterySaverSupported():
+    New API must be flagged with @FlaggedApi: method android.os.PowerManager.isBatterySaverSupported()
+UnflaggedApi: android.os.UserHandle#USER_CURRENT:
+    New API must be flagged with @FlaggedApi: field android.os.UserHandle.USER_CURRENT
+UnflaggedApi: android.os.UserManager#getAliveUsers():
+    New API must be flagged with @FlaggedApi: method android.os.UserManager.getAliveUsers()
+UnflaggedApi: android.os.UserManager#getUsers():
+    New API must be flagged with @FlaggedApi: method android.os.UserManager.getUsers()
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration:
+    New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.ParsedVibration
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration#getVibrationEffects():
+    New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.ParsedVibration.getVibrationEffects()
+UnflaggedApi: android.os.vibrator.persistence.ParsedVibration#resolve(android.os.Vibrator):
+    New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.ParsedVibration.resolve(android.os.Vibrator)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser:
+    New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlParser
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser#parseDocument(java.io.Reader):
+    New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlParser.parseDocument(java.io.Reader)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlParser#parseVibrationEffect(java.io.Reader):
+    New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlParser.parseVibrationEffect(java.io.Reader)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer:
+    New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlSerializer
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer):
+    New API must be flagged with @FlaggedApi: method android.os.vibrator.persistence.VibrationXmlSerializer.serialize(android.os.VibrationEffect,java.io.Writer)
+UnflaggedApi: android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException:
+    New API must be flagged with @FlaggedApi: class android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException
+UnflaggedApi: android.service.notification.NotificationRankingUpdate:
+    New API must be flagged with @FlaggedApi: class android.service.notification.NotificationRankingUpdate
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#CONTENTS_FILE_DESCRIPTOR:
+    New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.CONTENTS_FILE_DESCRIPTOR
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#PARCELABLE_WRITE_RETURN_VALUE:
+    New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.PARCELABLE_WRITE_RETURN_VALUE
+UnflaggedApi: android.service.notification.NotificationRankingUpdate#isFdNotNullAndClosed():
+    New API must be flagged with @FlaggedApi: method android.service.notification.NotificationRankingUpdate.isFdNotNullAndClosed()
+UnflaggedApi: android.telephony.TelephonyManager#HAL_SERVICE_SATELLITE:
+    New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE
+UnflaggedApi: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities:
+    New API must be flagged with @FlaggedApi: class android.telephony.ims.feature.MmTelFeature.MmTelCapabilities
+UnflaggedApi: android.text.MeasuredParagraph:
+    New API must be flagged with @FlaggedApi: class android.text.MeasuredParagraph
+UnflaggedApi: android.text.MeasuredParagraph#buildForStaticLayoutTest(android.text.TextPaint, android.graphics.text.LineBreakConfig, CharSequence, int, int, android.text.TextDirectionHeuristic, int, boolean, android.text.MeasuredParagraph.StyleRunCallback):
+    New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.buildForStaticLayoutTest(android.text.TextPaint,android.graphics.text.LineBreakConfig,CharSequence,int,int,android.text.TextDirectionHeuristic,int,boolean,android.text.MeasuredParagraph.StyleRunCallback)
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback:
+    New API must be flagged with @FlaggedApi: class android.text.MeasuredParagraph.StyleRunCallback
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback#onAppendReplacementRun(android.graphics.Paint, int, float):
+    New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.StyleRunCallback.onAppendReplacementRun(android.graphics.Paint,int,float)
+UnflaggedApi: android.text.MeasuredParagraph.StyleRunCallback#onAppendStyleRun(android.graphics.Paint, android.graphics.text.LineBreakConfig, int, boolean):
+    New API must be flagged with @FlaggedApi: method android.text.MeasuredParagraph.StyleRunCallback.onAppendStyleRun(android.graphics.Paint,android.graphics.text.LineBreakConfig,int,boolean)
+UnflaggedApi: android.view.Choreographer#getFrameTimeNanos():
+    New API must be flagged with @FlaggedApi: method android.view.Choreographer.getFrameTimeNanos()
+UnflaggedApi: android.view.InputDevice#getAssociatedDisplayId():
+    New API must be flagged with @FlaggedApi: method android.view.InputDevice.getAssociatedDisplayId()
+UnflaggedApi: android.view.InputDevice#getKeyboardLanguageTag():
+    New API must be flagged with @FlaggedApi: method android.view.InputDevice.getKeyboardLanguageTag()
+UnflaggedApi: android.view.InputDevice#getKeyboardLayoutType():
+    New API must be flagged with @FlaggedApi: method android.view.InputDevice.getKeyboardLayoutType()
+UnflaggedApi: android.view.MotionEvent.PointerCoords#isResampled():
+    New API must be flagged with @FlaggedApi: method android.view.MotionEvent.PointerCoords.isResampled()
+UnflaggedApi: android.view.WindowManager#replaceContentOnDisplayWithMirror(int, android.view.Window):
+    New API must be flagged with @FlaggedApi: method android.view.WindowManager.replaceContentOnDisplayWithMirror(int,android.view.Window)
+UnflaggedApi: android.view.WindowManager#replaceContentOnDisplayWithSc(int, android.view.SurfaceControl):
+    New API must be flagged with @FlaggedApi: method android.view.WindowManager.replaceContentOnDisplayWithSc(int,android.view.SurfaceControl)
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMaxDisplayRefreshRate:
+    New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMaxDisplayRefreshRate
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMinDisplayRefreshRate:
+    New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMinDisplayRefreshRate
+UnflaggedApi: android.view.accessibility.AccessibilityWindowInfo#UNDEFINED_WINDOW_ID:
+    New API must be flagged with @FlaggedApi: field android.view.accessibility.AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+UnflaggedApi: android.view.animation.AnimationUtils#lockAnimationClock(long, long):
+    New API must be flagged with @FlaggedApi: method android.view.animation.AnimationUtils.lockAnimationClock(long,long)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#getEnabledInputMethodListAsUser(android.os.UserHandle):
+    New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.getEnabledInputMethodListAsUser(android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#getEnabledInputMethodSubtypeListAsUser(String, boolean, android.os.UserHandle):
+    New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.getEnabledInputMethodSubtypeListAsUser(String,boolean,android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isCurrentRootView(android.view.View):
+    New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isCurrentRootView(android.view.View)
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isStylusHandwritingAvailableAsUser(android.os.UserHandle):
+    New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isStylusHandwritingAvailableAsUser(android.os.UserHandle)
+UnflaggedApi: android.view.inputmethod.InsertModeGesture:
+    New API must be flagged with @FlaggedApi: class android.view.inputmethod.InsertModeGesture
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#displayId:
+    New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.displayId
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#isVisible:
+    New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.isVisible
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#transform:
+    New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.transform
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 00e546a..c54a7d9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -36,6 +36,7 @@
 import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
 import static android.window.ConfigurationHelper.isDifferentDisplay;
 import static android.window.ConfigurationHelper.shouldUpdateResources;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
 import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
@@ -3663,10 +3664,9 @@
             int resultCode, Intent data) {
         if (DEBUG_RESULTS) Slog.v(TAG, "sendActivityResult: id=" + id
                 + " req=" + requestCode + " res=" + resultCode + " data=" + data);
-        ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+        final ArrayList<ResultInfo> list = new ArrayList<>();
         list.add(new ResultInfo(id, requestCode, resultCode, data));
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread,
-                activityToken);
+        final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
         clientTransaction.addCallback(ActivityResultItem.obtain(activityToken, list));
         try {
             mAppThread.scheduleTransaction(clientTransaction);
@@ -3720,7 +3720,19 @@
     /**  Core implementation of activity launch. */
     private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         ActivityInfo aInfo = r.activityInfo;
-        if (r.packageInfo == null) {
+
+        if (getInstrumentation() != null
+                && getInstrumentation().getContext() != null
+                && getInstrumentation().getContext().getApplicationInfo() != null
+                && getInstrumentation().isSdkSandboxAllowedToStartActivities()) {
+            // Activities launched from CTS-in-sandbox tests use a customized ApplicationInfo. See
+            // also {@link SdkSandboxManagerLocal#getSdkSandboxApplicationInfoForInstrumentation}.
+            r.packageInfo =
+                    getPackageInfo(
+                            getInstrumentation().getContext().getApplicationInfo(),
+                            mCompatibilityInfo,
+                            Context.CONTEXT_INCLUDE_CODE);
+        } else if (r.packageInfo == null) {
             r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
@@ -4429,7 +4441,7 @@
     }
 
     private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
-        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
         transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.token,
                 r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
                 /* dontReport */ false, /* autoEnteringPip */ false));
@@ -4437,7 +4449,7 @@
     }
 
     private void scheduleResume(ActivityClientRecord r) {
-        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
         transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(r.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false));
         executeTransaction(transaction);
@@ -6029,7 +6041,7 @@
         final ActivityLifecycleItem lifecycleRequest =
                 TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
         // Schedule the transaction.
-        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
         transaction.addCallback(activityRelaunchItem);
         transaction.setLifecycleStateRequest(lifecycleRequest);
         executeTransaction(transaction);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0255860..e5a73be 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2943,6 +2943,17 @@
         return isPackageSuspendedForUser(mContext.getOpPackageName(), getUserId());
     }
 
+    @Override
+    public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
+        try {
+            return mPM.isPackageQuarantinedForUser(packageName, getUserId());
+        } catch (IllegalArgumentException ie) {
+            throw new NameNotFoundException(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     @Override
     public void setApplicationCategoryHint(String packageName, int categoryHint) {
@@ -3355,7 +3366,11 @@
         }
         Drawable dr = null;
         if (itemInfo.packageName != null) {
-            dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+            if (itemInfo.isArchived) {
+                dr = getArchivedAppIcon(itemInfo.packageName);
+            } else {
+                dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+            }
         }
         if (dr == null && itemInfo != appInfo && appInfo != null) {
             dr = loadUnbadgedItemIcon(appInfo, appInfo);
@@ -3964,4 +3979,14 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    @Nullable
+    private Drawable getArchivedAppIcon(String packageName) {
+        try {
+            return new BitmapDrawable(null,
+                    mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId())));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 0b5a5ed1..1f5f2e4 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.annotation.SuppressLint;
@@ -108,13 +107,12 @@
             if (DBG) {
                 Log.d(TAG, "Task#" + i + ": activity=" + task.topActivity
                         + ", visible=" + task.isVisible()
-                        + ", flg=" + Integer.toHexString(task.baseIntent.getFlags()));
+                        + ", flg=" + Integer.toHexString(task.baseIntent.getFlags())
+                        + ", type=" + task.getActivityType());
             }
-            if (!task.isVisible()
-                    || (task.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
-                continue;
+            if (task.isVisible() && (task.getActivityType() == ACTIVITY_TYPE_HOME)) {
+                return true;
             }
-            return task.getActivityType() == ACTIVITY_TYPE_HOME;
         }
         return false;
     }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e31486f..10747bb 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerGlobal;
@@ -474,6 +475,56 @@
         sr.waitForComplete();
     }
 
+    boolean isSdkSandboxAllowedToStartActivities() {
+        return Process.isSdkSandbox()
+                && mThread != null
+                && mThread.mBoundApplication != null
+                && mThread.mBoundApplication.isSdkInSandbox
+                && getContext() != null
+                && (getContext()
+                                .checkSelfPermission(
+                                        android.Manifest.permission
+                                                .START_ACTIVITIES_FROM_SDK_SANDBOX)
+                        == PackageManager.PERMISSION_GRANTED);
+    }
+
+    /**
+     * Activity name resolution for CTS-in-SdkSandbox tests requires some adjustments. Intents
+     * generated using {@link Context#getPackageName()} use the SDK sandbox package name in the
+     * component field instead of the test package name. An SDK-in-sandbox test attempting to launch
+     * an activity in the test package will encounter name resolution errors when resolving the
+     * activity name in the SDK sandbox package.
+     *
+     * <p>This function replaces the package name of the input intent component to allow activities
+     * belonging to a CTS-in-sandbox test to resolve correctly.
+     *
+     * @param intent the intent to modify to allow CTS-in-sandbox activity resolution.
+     */
+    private void adjustIntentForCtsInSdkSandboxInstrumentation(@NonNull Intent intent) {
+        if (mComponent != null
+                && intent.getComponent() != null
+                && getContext()
+                        .getPackageManager()
+                        .getSdkSandboxPackageName()
+                        .equals(intent.getComponent().getPackageName())) {
+            // Resolve the intent target for the test package, not for the sandbox package.
+            intent.setComponent(
+                    new ComponentName(
+                            mComponent.getPackageName(), intent.getComponent().getClassName()));
+        }
+        // We match the intent identifier against the running instrumentations for the sandbox.
+        intent.setIdentifier(mComponent.getPackageName());
+    }
+
+    private ActivityInfo resolveActivityInfoForCtsInSandbox(@NonNull Intent intent) {
+        adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        ActivityInfo ai = intent.resolveActivityInfo(getTargetContext().getPackageManager(), 0);
+        if (ai != null) {
+            ai.processName = mThread.getProcessName();
+        }
+        return ai;
+    }
+
     /**
      * Start a new activity and wait for it to begin running before returning.
      * In addition to being synchronous, this method as some semantic
@@ -531,8 +582,10 @@
         synchronized (mSync) {
             intent = new Intent(intent);
 
-            ActivityInfo ai = intent.resolveActivityInfo(
-                getTargetContext().getPackageManager(), 0);
+            ActivityInfo ai =
+                    isSdkSandboxAllowedToStartActivities()
+                            ? resolveActivityInfoForCtsInSandbox(intent)
+                            : intent.resolveActivityInfo(getTargetContext().getPackageManager(), 0);
             if (ai == null) {
                 throw new RuntimeException("Unable to resolve activity for: " + intent);
             }
@@ -1842,6 +1895,9 @@
         if (referrer != null) {
             intent.putExtra(Intent.EXTRA_REFERRER, referrer);
         }
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
@@ -1914,6 +1970,11 @@
             IBinder token, Activity target, Intent[] intents, Bundle options,
             int userId) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            for (Intent intent : intents) {
+                adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+            }
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
@@ -1989,6 +2050,9 @@
         Context who, IBinder contextThread, IBinder token, String target,
         Intent intent, int requestCode, Bundle options) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
@@ -2060,6 +2124,9 @@
             Context who, IBinder contextThread, IBinder token, String resultWho,
             Intent intent, int requestCode, Bundle options, UserHandle user) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
@@ -2110,6 +2177,9 @@
             Intent intent, int requestCode, Bundle options,
             boolean ignoreTargetSecurity, int userId) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
@@ -2161,6 +2231,9 @@
             Context who, IBinder contextThread, IAppTask appTask,
             Intent intent, Bundle options) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index e447dc5..2d55403 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -49,7 +49,6 @@
 import android.util.Log;
 import android.view.IOnKeyguardExitResult;
 import android.view.IWindowManager;
-import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -71,9 +70,7 @@
 import java.util.concurrent.Executor;
 
 /**
- * Class that can be used to lock and unlock the keyguard. The
- * actual class to control the keyguard locking is
- * {@link android.app.KeyguardManager.KeyguardLock}.
+ * Class to manage and query the state of the lock screen (also known as Keyguard).
  */
 @SystemService(Context.KEYGUARD_SERVICE)
 public class KeyguardManager {
@@ -259,7 +256,9 @@
      * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
      *
      * @return the intent for launching the activity or null if no password is required.
-     * @deprecated see BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)
+     *
+     * @deprecated see {@link
+     *   android.hardware.biometrics.BiometricPrompt.Builder#setAllowedAuthenticators(int)}
      */
     @Deprecated
     @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
@@ -477,13 +476,12 @@
 
     /**
      * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
-     * you to disable / reenable the keyguard.
+     * you to temporarily disable / reenable the keyguard (lock screen).
      *
-     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
-     * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
-     * instead; this allows you to seamlessly hide the keyguard as your application
-     * moves in and out of the foreground and does not require that any special
-     * permissions be requested.
+     * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+     *   android.app.Activity#setShowWhenLocked(boolean)} instead. This allows you to seamlessly
+     *   occlude and unocclude the keyguard as your application moves in and out of the foreground
+     *   and does not require that any special permissions be requested.
      */
     @Deprecated
     public class KeyguardLock {
@@ -498,12 +496,12 @@
          * Disable the keyguard from showing.  If the keyguard is currently
          * showing, hide it.  The keyguard will be prevented from showing again
          * until {@link #reenableKeyguard()} is called.
-         *
+         * <p>
+         * This only works if the keyguard is not secure.
+         * <p>
          * A good place to call this is from {@link android.app.Activity#onResume()}
          *
-         * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
-         * is enabled that requires a password.
-         *
+         * @see KeyguardManager#isKeyguardSecure()
          * @see #reenableKeyguard()
          */
         @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
@@ -520,9 +518,6 @@
          *
          * A good place to call this is from {@link android.app.Activity#onPause()}
          *
-         * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
-         * is enabled that requires a password.
-         *
          * @see #disableKeyguard()
          */
         @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
@@ -621,20 +616,18 @@
     }
 
     /**
-     * Enables you to lock or unlock the keyguard. Get an instance of this class by
-     * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
-     * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
+     * Enables you to temporarily disable / reenable the keyguard (lock screen).
+     *
      * @param tag A tag that informally identifies who you are (for debugging who
      *   is disabling the keyguard).
      *
      * @return A {@link KeyguardLock} handle to use to disable and reenable the
      *   keyguard.
      *
-     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
-     *   and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
-     *   instead; this allows you to seamlessly hide the keyguard as your application
-     *   moves in and out of the foreground and does not require that any special
-     *   permissions be requested.
+     * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+     *   android.app.Activity#setShowWhenLocked(boolean)} instead. This allows you to seamlessly
+     *   occlude and unocclude the keyguard as your application moves in and out of the foreground
+     *   and does not require that any special permissions be requested.
      */
     @Deprecated
     public KeyguardLock newKeyguardLock(String tag) {
@@ -642,9 +635,36 @@
     }
 
     /**
-     * Return whether the keyguard is currently locked.
+     * Returns whether the lock screen (also known as Keyguard) is showing.
+     * <p>
+     * Specifically, this returns {@code true} in the following cases:
+     * <ul>
+     *   <li>The lock screen is showing in the foreground.</li>
+     *   <li>The lock screen is showing, but it is occluded by an activity that is showing on top of
+     *   it. A common example is the phone app receiving a call or making an emergency call.</li>
+     *   <li>The lock screen was showing but is temporarily disabled as a result of <a
+     *   href="https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode">lock task
+     *   mode</a> or an app using the deprecated {@link KeyguardLock} API.</li>
+     * </ul>
+     * <p>
+     * "Showing" refers to a logical state of the UI, regardless of whether the screen happens to be
+     * on. When the power button is pressed on an unlocked device, the lock screen starts "showing"
+     * immediately when the screen turns off.
+     * <p>
+     * This method does not distinguish a lock screen that is requiring authentication (e.g. with
+     * PIN, pattern, password, or biometric) from a lock screen that is trivially dismissible (e.g.
+     * with swipe). It also does not distinguish a lock screen requesting a SIM card PIN from a
+     * normal device lock screen. Finally, it always returns the global lock screen state and does
+     * not consider the {@link Context}'s user specifically.
+     * <p>
+     * Note that {@code isKeyguardLocked()} is confusingly named and probably should be called
+     * {@code isKeyguardShowing()}. On many devices, the lock screen displays an <i>unlocked</i>
+     * padlock icon when it is trivially dismissible. As mentioned above, {@code isKeyguardLocked()}
+     * actually returns {@code true} in this case, not {@code false} as might be expected. {@link
+     * #isDeviceLocked()} is an alternative API that has slightly different semantics.
      *
-     * @return {@code true} if the keyguard is locked.
+     * @return {@code true} if the lock screen is showing
+     * @see #isDeviceLocked()
      */
     public boolean isKeyguardLocked() {
         try {
@@ -655,12 +675,23 @@
     }
 
     /**
-     * Return whether the keyguard is secured by a PIN, pattern or password or a SIM card
-     * is currently locked.
+     * Returns whether the user has a secure lock screen or there is a locked SIM card.
+     * <p>
+     * Specifically, this returns {@code true} if at least one of the following is true:
+     * <ul>
+     *   <li>The {@link Context}'s user has a secure lock screen. A full user has a secure lock
+     *   screen if its lock screen is set to PIN, pattern, or password, as opposed to swipe or none.
+     *   A profile that uses a unified challenge is considered to have a secure lock screen if and
+     *   only if its parent user has a secure lock screen.</li>
+     *   <li>At least one SIM card is currently locked and requires a PIN.</li>
+     * </ul>
+     * <p>
+     * This method does not consider whether the lock screen is currently showing or not.
+     * <p>
+     * See also {@link #isDeviceSecure()} which excludes locked SIM cards.
      *
-     * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
-     *
-     * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked.
+     * @return {@code true} if the user has a secure lock screen or there is a locked SIM card
+     * @see #isDeviceSecure()
      */
     public boolean isKeyguardSecure() {
         try {
@@ -671,11 +702,11 @@
     }
 
     /**
-     * If keyguard screen is showing or in restricted key input mode (i.e. in
-     * keyguard password emergency screen). When in such mode, certain keys,
-     * such as the Home key and the right soft keys, don't work.
+     * Returns whether the lock screen is showing.
+     * <p>
+     * This is exactly the same as {@link #isKeyguardLocked()}.
      *
-     * @return {@code true} if in keyguard restricted input mode.
+     * @return the value of {@link #isKeyguardLocked()}
      * @deprecated Use {@link #isKeyguardLocked()} instead.
      */
     public boolean inKeyguardRestrictedInputMode() {
@@ -683,11 +714,26 @@
     }
 
     /**
-     * Returns whether the device is currently locked and requires a PIN, pattern or
-     * password to unlock.
+     * Returns whether the device is currently locked for the user.
+     * <p>
+     * This returns the device locked state for the {@link Context}'s user. If this user is the
+     * current user, then the device is considered "locked" when the lock screen is showing (i.e.
+     * {@link #isKeyguardLocked()} returns {@code true}) and is not trivially dismissible (e.g. with
+     * swipe), and the user has a PIN, pattern, or password.
+     * <p>
+     * Note: the above definition implies that a user with no PIN, pattern, or password is never
+     * considered locked, even if the lock screen is showing and requesting a SIM card PIN. The
+     * device PIN and SIM PIN are separate. Also, the user is not considered locked if face
+     * authentication has just completed or a trust agent is keeping the device unlocked, since in
+     * these cases the lock screen is dismissible with swipe.
+     * <p>
+     * For a user that is not the current user but can be switched to (usually this means "another
+     * full user"), and that has a PIN, pattern, or password, the device is always considered
+     * locked. For a profile with a unified challenge, the device is considered locked if and only
+     * if the device is locked for the parent user.
      *
-     * @return {@code true} if unlocking the device currently requires a PIN, pattern or
-     * password.
+     * @return {@code true} if the device is currently locked for the user
+     * @see #isKeyguardLocked()
      */
     public boolean isDeviceLocked() {
         return isDeviceLocked(mContext.getUserId());
@@ -708,12 +754,19 @@
     }
 
     /**
-     * Returns whether the device is secured with a PIN, pattern or
-     * password.
+     * Returns whether the user has a secure lock screen.
+     * <p>
+     * This returns {@code true} if the {@link Context}'s user has a secure lock screen. A full user
+     * has a secure lock screen if its lock screen is set to PIN, pattern, or password, as opposed
+     * to swipe or none. A profile that uses a unified challenge is considered to have a secure lock
+     * screen if and only if its parent user has a secure lock screen.
+     * <p>
+     * This method does not consider whether the lock screen is currently showing or not.
+     * <p>
+     * See also {@link #isKeyguardSecure()} which includes locked SIM cards.
      *
-     * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
-     *
-     * @return {@code true} if a PIN, pattern or password was set.
+     * @return {@code true} if the user has a secure lock screen
+     * @see #isKeyguardSecure()
      */
     public boolean isDeviceSecure() {
         return isDeviceSecure(mContext.getUserId());
@@ -734,8 +787,7 @@
     }
 
     /**
-     * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
-     * be dismissed.
+     * Requests that the Keyguard (lock screen) be dismissed if it is currently showing.
      * <p>
      * If the Keyguard is not secure or the device is currently in a trusted state, calling this
      * method will immediately dismiss the Keyguard without any user interaction.
@@ -746,8 +798,9 @@
      * If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
      * the screen will turn on when the keyguard is dismissed.
      *
-     * @param activity The activity requesting the dismissal. The activity must be either visible
-     *                 by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+     * @param activity The activity requesting the dismissal. The activity must either be visible
+     *                 by using {@link android.R.attr#showWhenLocked} or {@link
+     *                 android.app.Activity#setShowWhenLocked(boolean)}, or must be in a state in
      *                 which it would be visible if Keyguard would not be hiding it. If that's not
      *                 the case, the request will fail immediately and
      *                 {@link KeyguardDismissCallback#onDismissError} will be invoked.
@@ -762,8 +815,7 @@
     }
 
     /**
-     * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
-     * be dismissed.
+     * Requests that the Keyguard (lock screen) be dismissed if it is currently showing.
      * <p>
      * If the Keyguard is not secure or the device is currently in a trusted state, calling this
      * method will immediately dismiss the Keyguard without any user interaction.
@@ -774,8 +826,9 @@
      * If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
      * the screen will turn on when the keyguard is dismissed.
      *
-     * @param activity The activity requesting the dismissal. The activity must be either visible
-     *                 by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+     * @param activity The activity requesting the dismissal. The activity must either be visible
+     *                 by using {@link android.R.attr#showWhenLocked} or {@link
+     *                 android.app.Activity#setShowWhenLocked(boolean)}, or must be in a state in
      *                 which it would be visible if Keyguard would not be hiding it. If that's not
      *                 the case, the request will fail immediately and
      *                 {@link KeyguardDismissCallback#onDismissError} will be invoked.
@@ -829,12 +882,12 @@
      * @param callback Lets you know whether the operation was successful and
      *   it is safe to launch anything that would normally be considered safe
      *   once the user has gotten past the keyguard.
-
-     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
-     *   and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
-     *   instead; this allows you to seamlessly hide the keyguard as your application
-     *   moves in and out of the foreground and does not require that any special
-     *   permissions be requested.
+     *
+     * @deprecated Use {@link android.R.attr#showWhenLocked} or {@link
+     *   android.app.Activity#setShowWhenLocked(boolean)} to seamlessly occlude and unocclude the
+     *   keyguard as your application moves in and out of the foreground, without requiring any
+     *   special permissions. Use {@link #requestDismissKeyguard(android.app.Activity,
+     *   KeyguardDismissCallback)} to request dismissal of the keyguard.
      */
     @Deprecated
     @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cbbf4e0..fa52968 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1647,6 +1647,12 @@
                 case Context.VIRTUALIZATION_SERVICE:
                 case Context.VIRTUAL_DEVICE_SERVICE:
                     return null;
+                case Context.SEARCH_SERVICE:
+                    // Wear device does not support SEARCH_SERVICE so we do not print WTF here
+                    PackageManager manager = ctx.getPackageManager();
+                    if (manager != null && manager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+                        return null;
+                    }
             }
             Slog.wtf(TAG, "Manager wrapper not available: " + name);
             return null;
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 06e1b5b..93107ce 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -350,21 +350,6 @@
             "file_patterns": ["(/|^)ContextImpl.java"]
         },
         {
-            "name": "CtsWindowManagerDeviceScvh",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "exclude-annotation": "org.junit.Ignore"
-                },
-                {
-                    "include-filter": "android.content.wm.cts"
-                }
-            ],
-            "file_patterns": ["(/|^)ContextImpl.java"]
-        },
-        {
             "name": "CtsWindowManagerDeviceWindow",
             "options": [
                 {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 715edc5..213e5cb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11806,6 +11806,34 @@
     }
 
     /**
+     * Returns the list of {@link EnforcingAdmin}s who have set this restriction.
+     *
+     * <p>Note that for {@link #POLICY_SUSPEND_PACKAGES} it returns the PO or DO to keep the
+     * behavior the same as before the bug fix for b/192245204.
+     *
+     * <p>This API is only callable by the system UID
+     *
+     * @param userId      The user for whom to retrieve the information.
+     * @param restriction The restriction enforced by admins. It could be any user restriction or
+     *                    policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+     *                    {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+     *
+     * @hide
+     */
+    public @NonNull Set<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,
+            @NonNull String restriction) {
+        if (mService != null) {
+            try {
+                return new HashSet<>(mService.getEnforcingAdminsForRestriction(
+                        userId, restriction));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Hide or unhide packages. When a package is hidden it is unavailable for use, but the data and
      * actual package file remain. This function can be called by a device owner, profile owner, or
      * by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
diff --git a/core/java/android/app/admin/EnforcingAdmin.aidl b/core/java/android/app/admin/EnforcingAdmin.aidl
new file mode 100644
index 0000000..bfbfdbe
--- /dev/null
+++ b/core/java/android/app/admin/EnforcingAdmin.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.admin;
+
+parcelable EnforcingAdmin;
\ No newline at end of file
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index 771794d..7c718f6 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -38,6 +39,11 @@
     private final UserHandle mUserHandle;
 
     /**
+     * @hide
+     */
+    private final ComponentName mComponentName;
+
+    /**
      * Creates an enforcing admin with the given params.
      */
     public EnforcingAdmin(
@@ -46,6 +52,21 @@
         mPackageName = Objects.requireNonNull(packageName);
         mAuthority = Objects.requireNonNull(authority);
         mUserHandle = Objects.requireNonNull(userHandle);
+        mComponentName = null;
+    }
+
+    /**
+     * Creates an enforcing admin with the given params.
+     *
+     * @hide
+     */
+    public EnforcingAdmin(
+            @NonNull String packageName, @NonNull Authority authority,
+            @NonNull UserHandle userHandle, @Nullable ComponentName componentName) {
+        mPackageName = Objects.requireNonNull(packageName);
+        mAuthority = Objects.requireNonNull(authority);
+        mUserHandle = Objects.requireNonNull(userHandle);
+        mComponentName = componentName;
     }
 
     private EnforcingAdmin(Parcel source) {
@@ -53,6 +74,7 @@
         mUserHandle = new UserHandle(source.readInt());
         mAuthority = Objects.requireNonNull(
                 source.readParcelable(Authority.class.getClassLoader()));
+        mComponentName = source.readParcelable(ComponentName.class.getClassLoader());
     }
 
     /**
@@ -86,7 +108,8 @@
         EnforcingAdmin other = (EnforcingAdmin) o;
         return Objects.equals(mPackageName, other.mPackageName)
                 && Objects.equals(mAuthority, other.mAuthority)
-                && Objects.equals(mUserHandle, other.mUserHandle);
+                && Objects.equals(mUserHandle, other.mUserHandle)
+                && Objects.equals(mComponentName, other.mComponentName);
     }
 
     @Override
@@ -97,7 +120,7 @@
     @Override
     public String toString() {
         return "EnforcingAdmin { mPackageName= " + mPackageName + ", mAuthority= " + mAuthority
-                + ", mUserHandle= " + mUserHandle + " }";
+                + ", mUserHandle= " + mUserHandle + ", mComponentName= " + mComponentName + " }";
     }
 
     @Override
@@ -110,6 +133,7 @@
         dest.writeString(mPackageName);
         dest.writeInt(mUserHandle.getIdentifier());
         dest.writeParcelable(mAuthority, flags);
+        dest.writeParcelable(mComponentName, flags);
     }
 
     @NonNull
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 95ec89e..c49b820 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -54,6 +54,7 @@
 import android.telephony.data.ApnSetting;
 import com.android.internal.infra.AndroidFuture;
 import android.app.admin.DevicePolicyState;
+import android.app.admin.EnforcingAdmin;
 
 import java.util.List;
 
@@ -274,6 +275,7 @@
 
     Intent createAdminSupportIntent(in String restriction);
     Bundle getEnforcingAdminAndUserDetails(int userId,String restriction);
+    List<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,String restriction);
     boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
     boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
 
diff --git a/core/java/android/app/admin/Provisioning_OWNERS b/core/java/android/app/admin/Provisioning_OWNERS
index fa0a1f0..8f71fc0 100644
--- a/core/java/android/app/admin/Provisioning_OWNERS
+++ b/core/java/android/app/admin/Provisioning_OWNERS
@@ -1,4 +1,4 @@
 # Assign bugs to android-enterprise-triage@google.com
-mdb.ae-provisioning-reviews@google.com
+ae-provisioning-reviews@google.com
+petuska@google.com #{LAST_RESORT_SUGGESTION}
 file:EnterprisePlatform_OWNERS
-petuska@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index c2c5427..dd332c8 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -41,7 +41,7 @@
     private Configuration mConfiguration;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @Nullable IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
         // Notify the client of an upcoming change in the token configuration. This ensures that
         // batches of config change items only process the newest configuration.
@@ -59,8 +59,7 @@
 
     @Nullable
     @Override
-    public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
-            @Nullable IBinder token) {
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
         return client.getActivity(getActivityToken());
     }
 
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 491d026..a5dd115 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -56,7 +56,7 @@
     private ActivityClientRecord mActivityClientRecord;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         // The local config is already scaled so only apply if this item is from server side.
         if (!client.isExecutingLocalTransaction()) {
             CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
@@ -78,7 +78,7 @@
     }
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         final ActivityClientRecord r = getActivityClientRecord(client);
         client.reportRelaunch(r);
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index 0f8879e..2a65b35 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -16,6 +16,8 @@
 
 package android.app.servertransaction;
 
+import static android.app.servertransaction.TransactionExecutorHelper.getActivityName;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
 import android.annotation.CallSuper;
@@ -28,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
 import java.util.Objects;
 
 /**
@@ -49,14 +52,14 @@
     ActivityTransactionItem() {}
 
     @Override
-    public final void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public final void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         final ActivityClientRecord r = getActivityClientRecord(client);
         execute(client, r, pendingActions);
     }
 
     /**
-     * Like {@link #execute(ClientTransactionHandler, IBinder, PendingTransactionActions)},
+     * Like {@link #execute(ClientTransactionHandler, PendingTransactionActions)},
      * but take non-null {@link ActivityClientRecord} as a parameter.
      */
     @VisibleForTesting(visibility = PACKAGE)
@@ -111,6 +114,14 @@
         mActivityToken = null;
     }
 
+    @Override
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw,
+            @NonNull ClientTransactionHandler transactionHandler) {
+        super.dump(prefix, pw, transactionHandler);
+        pw.append(prefix).append("Target activity: ")
+                .println(getActivityName(mActivityToken, transactionHandler));
+    }
+
     // Subclass must override and call super.equals to compare the mActivityToken.
     @SuppressWarnings("EqualsGetClass")
     @CallSuper
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
index c91e0ca..f275175 100644
--- a/core/java/android/app/servertransaction/BaseClientRequest.java
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -16,8 +16,8 @@
 
 package android.app.servertransaction;
 
+import android.annotation.NonNull;
 import android.app.ClientTransactionHandler;
-import android.os.IBinder;
 
 /**
  * Base interface for individual requests from server to client.
@@ -27,31 +27,28 @@
 public interface BaseClientRequest extends ObjectPoolItem {
 
     /**
-     * Prepare the client request before scheduling.
+     * Prepares the client request before scheduling.
      * An example of this might be informing about pending updates for some values.
      *
      * @param client Target client handler.
-     * @param token  Target activity token.
      */
-    default void preExecute(ClientTransactionHandler client, IBinder token) {
+    default void preExecute(@NonNull ClientTransactionHandler client) {
     }
 
     /**
-     * Execute the request.
+     * Executes the request.
      * @param client Target client handler.
-     * @param token Target activity token.
      * @param pendingActions Container that may have data pending to be used.
      */
-    void execute(ClientTransactionHandler client, IBinder token,
-            PendingTransactionActions pendingActions);
+    void execute(@NonNull ClientTransactionHandler client,
+            @NonNull PendingTransactionActions pendingActions);
 
     /**
-     * Perform all actions that need to happen after execution, e.g. report the result to server.
+     * Performs all actions that need to happen after execution, e.g. report the result to server.
      * @param client Target client handler.
-     * @param token Target activity token.
      * @param pendingActions Container that may have data pending to be used.
      */
-    default void postExecute(ClientTransactionHandler client, IBinder token,
-            PendingTransactionActions pendingActions) {
+    default void postExecute(@NonNull ClientTransactionHandler client,
+            @NonNull PendingTransactionActions pendingActions) {
     }
 }
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index ee14708..a5b0f18 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -16,6 +16,9 @@
 
 package android.app.servertransaction;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
 import android.app.IApplicationThread;
@@ -36,7 +39,7 @@
  * A container that holds a sequence of messages, which may be sent to a client.
  * This includes a list of callbacks and a final lifecycle state.
  *
- * @see com.android.server.am.ClientLifecycleManager
+ * @see com.android.server.wm.ClientLifecycleManager
  * @see ClientTransactionItem
  * @see ActivityLifecycleItem
  * @hide
@@ -56,9 +59,6 @@
     /** Target client. */
     private IApplicationThread mClient;
 
-    /** Target client activity. Might be null if the entire transaction is targeting an app. */
-    private IBinder mActivityToken;
-
     /** Get the target client of the transaction. */
     public IApplicationThread getClient() {
         return mClient;
@@ -68,7 +68,7 @@
      * Add a message to the end of the sequence of callbacks.
      * @param activityCallback A single message that can contain a lifecycle request/callback.
      */
-    public void addCallback(ClientTransactionItem activityCallback) {
+    public void addCallback(@NonNull ClientTransactionItem activityCallback) {
         if (mActivityCallbacks == null) {
             mActivityCallbacks = new ArrayList<>();
         }
@@ -87,11 +87,21 @@
     @Nullable
     @UnsupportedAppUsage
     public IBinder getActivityToken() {
-        return mActivityToken;
+        // TODO(b/260873529): remove after we allow multiple activity items in one transaction.
+        if (mLifecycleStateRequest != null) {
+            return mLifecycleStateRequest.getActivityToken();
+        }
+        for (int i = mActivityCallbacks.size() - 1; i >= 0; i--) {
+            final IBinder token = mActivityCallbacks.get(i).getActivityToken();
+            if (token != null) {
+                return token;
+            }
+        }
+        return null;
     }
 
     /** Get the target state lifecycle request. */
-    @VisibleForTesting
+    @VisibleForTesting(visibility = PACKAGE)
     @UnsupportedAppUsage
     public ActivityLifecycleItem getLifecycleStateRequest() {
         return mLifecycleStateRequest;
@@ -101,7 +111,7 @@
      * Set the lifecycle state in which the client should be after executing the transaction.
      * @param stateRequest A lifecycle request initialized with right parameters.
      */
-    public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
+    public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
         mLifecycleStateRequest = stateRequest;
     }
 
@@ -110,15 +120,15 @@
      * @param clientTransactionHandler Handler on the client side that will executed all operations
      *                                 requested by transaction items.
      */
-    public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
+    public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
         if (mActivityCallbacks != null) {
             final int size = mActivityCallbacks.size();
             for (int i = 0; i < size; ++i) {
-                mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
+                mActivityCallbacks.get(i).preExecute(clientTransactionHandler);
             }
         }
         if (mLifecycleStateRequest != null) {
-            mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
+            mLifecycleStateRequest.preExecute(clientTransactionHandler);
         }
     }
 
@@ -141,14 +151,14 @@
 
     private ClientTransaction() {}
 
-    /** Obtain an instance initialized with provided params. */
-    public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
+    /** Obtains an instance initialized with provided params. */
+    @NonNull
+    public static ClientTransaction obtain(@Nullable IApplicationThread client) {
         ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
         if (instance == null) {
             instance = new ClientTransaction();
         }
         instance.mClient = client;
-        instance.mActivityToken = activityToken;
 
         return instance;
     }
@@ -167,7 +177,6 @@
             mLifecycleStateRequest = null;
         }
         mClient = null;
-        mActivityToken = null;
         ObjectPool.recycle(this);
     }
 
@@ -175,12 +184,7 @@
 
     /** Write to Parcel. */
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        final boolean writeActivityToken = mActivityToken != null;
-        dest.writeBoolean(writeActivityToken);
-        if (writeActivityToken) {
-            dest.writeStrongBinder(mActivityToken);
-        }
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelable(mLifecycleStateRequest, flags);
         final boolean writeActivityCallbacks = mActivityCallbacks != null;
         dest.writeBoolean(writeActivityCallbacks);
@@ -190,11 +194,7 @@
     }
 
     /** Read from Parcel. */
-    private ClientTransaction(Parcel in) {
-        final boolean readActivityToken = in.readBoolean();
-        if (readActivityToken) {
-            mActivityToken = in.readStrongBinder();
-        }
+    private ClientTransaction(@NonNull Parcel in) {
         mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class);
         final boolean readActivityCallbacks = in.readBoolean();
         if (readActivityCallbacks) {
@@ -203,9 +203,8 @@
         }
     }
 
-    public static final @android.annotation.NonNull Creator<ClientTransaction> CREATOR =
-            new Creator<ClientTransaction>() {
-        public ClientTransaction createFromParcel(Parcel in) {
+    public static final @NonNull Creator<ClientTransaction> CREATOR = new Creator<>() {
+        public ClientTransaction createFromParcel(@NonNull Parcel in) {
             return new ClientTransaction(in);
         }
 
@@ -230,8 +229,7 @@
         final ClientTransaction other = (ClientTransaction) o;
         return Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
                 && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
-                && mClient == other.mClient
-                && mActivityToken == other.mActivityToken;
+                && mClient == other.mClient;
     }
 
     @Override
@@ -240,26 +238,32 @@
         result = 31 * result + Objects.hashCode(mActivityCallbacks);
         result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
         result = 31 * result + Objects.hashCode(mClient);
-        result = 31 * result + Objects.hashCode(mActivityToken);
         return result;
     }
 
     /** Dump transaction items callback items and final lifecycle state request. */
-    public void dump(String prefix, PrintWriter pw) {
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw,
+            @NonNull ClientTransactionHandler transactionHandler) {
         pw.append(prefix).println("ClientTransaction{");
         pw.append(prefix).print("  callbacks=[");
+        final String itemPrefix = prefix + "    ";
         final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
         if (size > 0) {
             pw.println();
             for (int i = 0; i < size; i++) {
-                pw.append(prefix).append("    ").println(mActivityCallbacks.get(i).toString());
+                mActivityCallbacks.get(i).dump(itemPrefix, pw, transactionHandler);
             }
             pw.append(prefix).println("  ]");
         } else {
             pw.println("]");
         }
-        pw.append(prefix).append("  stateRequest=").println(mLifecycleStateRequest != null
-                ? mLifecycleStateRequest.toString() : null);
+
+        pw.append(prefix).println("  stateRequest=");
+        if (mLifecycleStateRequest != null) {
+            mLifecycleStateRequest.dump(itemPrefix, pw, transactionHandler);
+        } else {
+            pw.append(itemPrefix).println("null");
+        }
         pw.append(prefix).println("}");
     }
 }
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index 30fc104..07e5a7d 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -30,6 +30,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
+
 /**
  * A callback message to a client that can be scheduled and executed.
  * Examples of these might be activity configuration change, multi-window mode change, activity
@@ -56,8 +58,7 @@
      * it is updating; otherwise, returns {@code null}.
      */
     @Nullable
-    public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
-            @NonNull IBinder token) {
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
         return null;
     }
 
@@ -71,6 +72,12 @@
         return null;
     }
 
+    /** Dumps this transaction item. */
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw,
+            @NonNull ClientTransactionHandler transactionHandler) {
+        pw.append(prefix).println(this);
+    }
+
     // Parcelable
 
     @Override
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index f72e2e0..96961ace 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.os.IBinder;
 import android.os.Parcel;
 
 import java.util.Objects;
@@ -38,21 +37,20 @@
     private int mDeviceId;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @Nullable IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
         client.updatePendingConfiguration(mConfiguration);
     }
 
     @Override
-    public void execute(@NonNull ClientTransactionHandler client, @Nullable IBinder token,
+    public void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         client.handleConfigurationChanged(mConfiguration, mDeviceId);
     }
 
     @Nullable
     @Override
-    public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
-            @Nullable IBinder token) {
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
         return ActivityThread.currentApplication();
     }
 
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index a327a99..ddb6df1 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -36,7 +36,7 @@
     private int mConfigChanges;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         client.getActivitiesToBeDestroyed().put(getActivityToken(), this);
     }
 
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 9b37a35..a64c744 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -84,7 +84,7 @@
     private IActivityClientController mActivityClientController;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         client.countLaunchingActivities(1);
         client.updateProcessState(mProcState, false);
         CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig);
@@ -96,7 +96,7 @@
     }
 
     @Override
-    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
         ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo,
@@ -109,7 +109,7 @@
     }
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         client.countLaunchingActivities(-1);
     }
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index fb57bed..e56d3f8 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -40,7 +40,7 @@
     private Configuration mConfiguration;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
         // Notify the client of an upcoming change in the token configuration. This ensures that
         // batches of config change items only process the newest configuration.
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index a8e6772..8f1e90b 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         if (mDontReport) {
             return;
diff --git a/core/java/android/app/servertransaction/RefreshCallbackItem.java b/core/java/android/app/servertransaction/RefreshCallbackItem.java
index 00128f0..368ed76 100644
--- a/core/java/android/app/servertransaction/RefreshCallbackItem.java
+++ b/core/java/android/app/servertransaction/RefreshCallbackItem.java
@@ -51,7 +51,7 @@
             @NonNull ActivityClientRecord r, @NonNull PendingTransactionActions pendingActions) {}
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         final ActivityClientRecord r = getActivityClientRecord(client);
         client.reportRefresh(r);
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index b11e73c..4a0ea98 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -44,7 +44,7 @@
     private boolean mShouldSendCompatFakeFocus;
 
     @Override
-    public void preExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token) {
+    public void preExecute(@NonNull ClientTransactionHandler client) {
         if (mUpdateProcState) {
             client.updateProcessState(mProcState, false);
         }
@@ -60,7 +60,7 @@
     }
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         // TODO(lifecycler): Use interface callback instead of actual implementation.
         ActivityClient.getInstance().activityResumed(getActivityToken(),
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index f432567..b8ce52d 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -46,7 +46,7 @@
     }
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         client.reportStop(pendingActions);
     }
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index 693599f..23d4505 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -43,7 +43,7 @@
     }
 
     @Override
-    public void postExecute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         if (mOnTop) {
             return;
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index d080162..4433673 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -74,7 +74,7 @@
      * Then the client will cycle to the final lifecycle state if provided. Otherwise, it will
      * either remain in the initial state, or last state needed by a callback.
      */
-    public void execute(ClientTransaction transaction) {
+    public void execute(@NonNull ClientTransaction transaction) {
         if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
 
         final IBinder token = transaction.getActivityToken();
@@ -109,7 +109,7 @@
 
     /** Cycle through all states requested by callbacks and execute them at proper times. */
     @VisibleForTesting
-    public void executeCallbacks(ClientTransaction transaction) {
+    public void executeCallbacks(@NonNull ClientTransaction transaction) {
         final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
         if (callbacks == null || callbacks.isEmpty()) {
             // No callbacks to execute, return early.
@@ -117,9 +117,6 @@
         }
         if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
 
-        final IBinder token = transaction.getActivityToken();
-        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
-
         // In case when post-execution state of the last callback matches the final state requested
         // for the activity in this transaction, we won't do the last transition here and do it when
         // moving to final state instead (because it may contain additional parameters from server).
@@ -135,6 +132,9 @@
         final int size = callbacks.size();
         for (int i = 0; i < size; ++i) {
             final ClientTransactionItem item = callbacks.get(i);
+            final IBinder token = item.getActivityToken();
+            ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
             if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
             final int postExecutionState = item.getPostExecutionState();
 
@@ -150,13 +150,13 @@
             final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated()
                     && syncWindowConfigUpdateFlag();
             final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled
-                    ? item.getContextToUpdate(mTransactionHandler, token)
+                    ? item.getContextToUpdate(mTransactionHandler)
                     : null;
             final Configuration preExecutedConfig = configUpdatedContext != null
                     ? new Configuration(configUpdatedContext.getResources().getConfiguration())
                     : null;
 
-            item.execute(mTransactionHandler, token, mPendingActions);
+            item.execute(mTransactionHandler, mPendingActions);
 
             if (configUpdatedContext != null) {
                 final Configuration postExecutedConfig = configUpdatedContext.getResources()
@@ -169,7 +169,7 @@
                 }
             }
 
-            item.postExecute(mTransactionHandler, token, mPendingActions);
+            item.postExecute(mTransactionHandler, mPendingActions);
             if (r == null) {
                 // Launch activity request will create an activity record.
                 r = mTransactionHandler.getActivityClient(token);
@@ -195,14 +195,14 @@
     }
 
     /** Transition to the final state if requested by the transaction. */
-    private void executeLifecycleState(ClientTransaction transaction) {
+    private void executeLifecycleState(@NonNull ClientTransaction transaction) {
         final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
         if (lifecycleItem == null) {
             // No lifecycle request, return early.
             return;
         }
 
-        final IBinder token = transaction.getActivityToken();
+        final IBinder token = lifecycleItem.getActivityToken();
         final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
         if (DEBUG_RESOLVER) {
             Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
@@ -219,8 +219,8 @@
         cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
 
         // Execute the final transition with proper parameters.
-        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
-        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
+        lifecycleItem.execute(mTransactionHandler, mPendingActions);
+        lifecycleItem.postExecute(mTransactionHandler, mPendingActions);
     }
 
     /** Transition the client between states. */
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 0f9c517..7e89a5b 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,6 +26,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -266,14 +267,12 @@
     }
 
     /** Dump transaction to string. */
-    static String transactionToString(ClientTransaction transaction,
-            ClientTransactionHandler transactionHandler) {
+    static String transactionToString(@NonNull ClientTransaction transaction,
+            @NonNull ClientTransactionHandler transactionHandler) {
         final StringWriter stringWriter = new StringWriter();
         final PrintWriter pw = new PrintWriter(stringWriter);
         final String prefix = tId(transaction);
-        transaction.dump(prefix, pw);
-        pw.append(prefix + "Target activity: ")
-                .println(getActivityName(transaction.getActivityToken(), transactionHandler));
+        transaction.dump(prefix, pw, transactionHandler);
         return stringWriter.toString();
     }
 
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
index 99824b0..375d1bf 100644
--- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -41,15 +41,14 @@
     private WindowContextInfo mInfo;
 
     @Override
-    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         client.handleWindowContextInfoChanged(mClientToken, mInfo);
     }
 
     @Nullable
     @Override
-    public Context getContextToUpdate(@NonNull ClientTransactionHandler client,
-            @Nullable IBinder token) {
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
         return client.getWindowContext(mClientToken);
     }
 
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
index ed52a64..1bea468 100644
--- a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -36,7 +36,7 @@
     private IBinder mClientToken;
 
     @Override
-    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+    public void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         client.handleWindowContextWindowRemoval(mClientToken);
     }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d6dee93..b6a98a5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4089,6 +4089,7 @@
             VIBRATOR_MANAGER_SERVICE,
             VIBRATOR_SERVICE,
             //@hide: STATUS_BAR_SERVICE,
+            THREAD_NETWORK_SERVICE,
             CONNECTIVITY_SERVICE,
             PAC_PROXY_SERVICE,
             VCN_MANAGEMENT_SERVICE,
@@ -4764,6 +4765,20 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.net.thread.ThreadNetworkManager}.
+     *
+     * <p>On devices without {@link PackageManager#FEATURE_THREAD_NETWORK} system feature
+     * the {@link #getSystemService(String)} will return {@code null}.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.thread.ThreadNetworkManager
+     * @hide
+     */
+    @SystemApi
+    public static final String THREAD_NETWORK_SERVICE = "thread_network";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
      * IPSec.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e9bbed3..7579d99 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12516,6 +12516,7 @@
         return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
     }
 
+    // TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal}
     /** @hide */
     public boolean isSandboxActivity(@NonNull Context context) {
         if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
diff --git a/core/java/android/content/om/OWNERS b/core/java/android/content/om/OWNERS
index 3669817..72aed2d 100644
--- a/core/java/android/content/om/OWNERS
+++ b/core/java/android/content/om/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 568631
 
-toddke@android.com
-toddke@google.com
 patb@google.com
 zyy@google.com
+jakmcbane@google.com
\ No newline at end of file
diff --git a/core/java/android/content/pm/ArchivedActivityParcel.aidl b/core/java/android/content/pm/ArchivedActivityParcel.aidl
new file mode 100644
index 0000000..7ab7ed1
--- /dev/null
+++ b/core/java/android/content/pm/ArchivedActivityParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.content.pm;
+
+/** @hide */
+parcelable ArchivedActivityParcel {
+    String title;
+    // PNG compressed bitmaps.
+    byte[] iconBitmap;
+    byte[] monochromeIconBitmap;
+}
diff --git a/core/java/android/content/pm/ArchivedPackageParcel.aidl b/core/java/android/content/pm/ArchivedPackageParcel.aidl
index 573e690..d3cd79e 100644
--- a/core/java/android/content/pm/ArchivedPackageParcel.aidl
+++ b/core/java/android/content/pm/ArchivedPackageParcel.aidl
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.content.pm.ArchivedActivityParcel;
 import android.content.pm.SigningDetails;
 
 /**
@@ -29,9 +30,8 @@
     int versionCode;
     int versionCodeMajor;
     int targetSdkVersion;
-    String backupAllowed;
     String defaultToDeviceProtectedStorage;
     String requestLegacyExternalStorage;
     String userDataFragile;
-    String clearUserDataOnFailedRestoreAllowed;
+    ArchivedActivityParcel[] archivedActivities;
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 556c794..aca88d6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ArchivedPackageParcel;
@@ -59,7 +60,7 @@
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
-import android.content.IntentSender;
+import android.os.UserHandle;
 
 import java.util.Map;
 
@@ -832,5 +833,7 @@
 
     void unregisterPackageMonitorCallback(IRemoteCallback callback);
 
-    ArchivedPackageParcel getArchivedPackage(in String apkPath);
+    ArchivedPackageParcel getArchivedPackage(in String packageName, int userId);
+
+    Bitmap getArchivedAppIcon(String packageName, in UserHandle user);
 }
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 1fe1923..63c11b7 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -18,9 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.IntentSender;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -490,18 +488,6 @@
      */
     public boolean isActiveApex;
 
-    /**
-     * Whether the package is currently in an archived state.
-     *
-     * <p>Packages can be archived through
-     * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
-     * on the device, but do keep the data directory.
-     * @hide
-     */
-    // TODO(b/278553670) Unhide and update @links before launch.
-    @SystemApi
-    public boolean isArchived;
-
     public PackageInfo() {
     }
 
@@ -589,7 +575,6 @@
         }
         dest.writeBoolean(isApex);
         dest.writeBoolean(isActiveApex);
-        dest.writeBoolean(isArchived);
         dest.restoreAllowSquashing(prevAllowSquashing);
     }
 
@@ -655,6 +640,5 @@
         }
         isApex = source.readBoolean();
         isActiveApex = source.readBoolean();
-        isArchived = source.readBoolean();
     }
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 673a8a5..3a9e9bf 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -371,6 +371,13 @@
             "android.content.pm.extra.UNARCHIVE_ALL_USERS";
 
     /**
+     * A list of warnings that occurred during installation.
+     *
+     * @hide
+     */
+    public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
+
+    /**
      * Streaming installation pending.
      * Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
      *
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index bb978e0..c7091ad 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -21,6 +21,7 @@
 import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
 import static android.text.TextUtils.makeSafeForPresentation;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -173,6 +174,18 @@
      */
     public int showUserIcon;
 
+    /**
+     * Whether the package is currently in an archived state.
+     *
+     * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
+     * on the device, but do keep the data directory.
+     * @hide
+     */
+    // TODO(b/278553670) Unhide and update @links before launch.
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ARCHIVING)
+    public boolean isArchived;
+
     public PackageItemInfo() {
         showUserIcon = UserHandle.USER_NULL;
     }
@@ -189,6 +202,7 @@
         logo = orig.logo;
         metaData = orig.metaData;
         showUserIcon = orig.showUserIcon;
+        isArchived = orig.isArchived;
     }
 
     /**
@@ -442,6 +456,7 @@
         dest.writeBundle(metaData);
         dest.writeInt(banner);
         dest.writeInt(showUserIcon);
+        dest.writeBoolean(isArchived);
     }
 
     /**
@@ -459,6 +474,7 @@
         }
         proto.write(PackageItemInfoProto.ICON, icon);
         proto.write(PackageItemInfoProto.BANNER, banner);
+        proto.write(PackageItemInfoProto.IS_ARCHIVED, isArchived);
         proto.end(token);
     }
 
@@ -473,6 +489,7 @@
         metaData = source.readBundle();
         banner = source.readInt();
         showUserIcon = source.readInt();
+        isArchived = source.readBoolean();
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cdab431..3fc515d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2549,6 +2549,15 @@
     public static final int DELETE_DONT_KILL_APP = 0x00000008;
 
     /**
+     * Flag parameter for {@link #deletePackage} to indicate that the deletion is an archival. This
+     * flag is only for internal usage as part of
+     * {@link PackageInstaller#requestArchive(String, IntentSender)}.
+     *
+     * @hide
+     */
+    public static final int DELETE_ARCHIVE = 0x00000010;
+
+    /**
      * Flag parameter for {@link #deletePackage} to indicate that package deletion
      * should be chatty.
      *
@@ -9868,6 +9877,18 @@
     }
 
     /**
+     * Query if an app is currently quarantined.
+     *
+     * @return {@code true} if the given package is quarantined, {@code false} otherwise
+     * @throws NameNotFoundException if the package could not be found.
+     *
+     * @hide
+     */
+    public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
+        throw new UnsupportedOperationException("isPackageQuarantined not implemented");
+    }
+
+    /**
      * Provide a hint of what the {@link ApplicationInfo#category} value should
      * be for the given package.
      * <p>
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 149de7e..0333942 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -834,4 +834,11 @@
 
     public abstract V parseServiceAttributes(Resources res,
             String packageName, AttributeSet attrs);
+
+    @VisibleForTesting
+    public void unregisterReceivers() {
+        mContext.unregisterReceiver(mPackageReceiver);
+        mContext.unregisterReceiver(mExternalReceiver);
+        mContext.unregisterReceiver(mUserRemovedReceiver);
+    }
 }
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index be8b2a2..65f56f6 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -486,7 +486,7 @@
      * Here is an example:
      * <pre>
      *  &lt;uses-permission
-     *      android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+     *      android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
      *  /&gt;
      *  &lt;service
      *      android:name=".MySpecialForegroundService"
@@ -506,7 +506,7 @@
      * in both platforms.
      * <pre>
      *  &lt;uses-permission
-     *      android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+     *      android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
      *      android:maxSdkVersion="last_sdk_version_without_type_foo"
      *  /&gt;
      *  &lt;service
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 159b789..f3194be 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -23,11 +23,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
-import android.os.Build;
 
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
-import com.android.internal.util.XmlUtils;
 
 import java.util.List;
 import java.util.Set;
@@ -142,32 +140,9 @@
     private final boolean mIsSdkLibrary;
 
     /**
-     *  Set to <code>false</code> if the application does not wish to permit any OS-driven
-     *  backups of its data; <code>true</code> otherwise.
+     * Archival install info.
      */
-    private final boolean mBackupAllowed;
-
-    /**
-     * When set, the default data storage directory for this app is pointed at
-     * the device-protected location.
-     */
-    private final boolean mDefaultToDeviceProtectedStorage;
-
-    /**
-     * If {@code true} this app requests full external storage access.
-     */
-    private final boolean mRequestLegacyExternalStorage;
-
-    /**
-     * Indicates whether this application has declared its user data as fragile, causing the
-     * system to prompt the user on whether to keep the user data on uninstall.
-     */
-    private final boolean mUserDataFragile;
-
-    /**
-     * Indicates whether this application's data will be cleared on a failed restore.
-     */
-    private final boolean mClearUserDataOnFailedRestoreAllowed;
+    private final @Nullable ArchivedPackageParcel mArchivedPackage;
 
     public ApkLite(String path, String packageName, String splitName, boolean isFeatureSplit,
             String configForSplit, String usesSplitName, boolean isSplitRequired, int versionCode,
@@ -179,10 +154,7 @@
             String requiredSystemPropertyName, String requiredSystemPropertyValue,
             int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
             Set<String> requiredSplitTypes, Set<String> splitTypes,
-            boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean clearUserDataAllowed,
-            boolean backupAllowed, boolean defaultToDeviceProtectedStorage,
-            boolean requestLegacyExternalStorage, boolean userDataFragile,
-            boolean clearUserDataOnFailedRestoreAllowed) {
+            boolean hasDeviceAdminReceiver, boolean isSdkLibrary) {
         mPath = path;
         mPackageName = packageName;
         mSplitName = splitName;
@@ -216,11 +188,7 @@
         mRollbackDataPolicy = rollbackDataPolicy;
         mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
         mIsSdkLibrary = isSdkLibrary;
-        mBackupAllowed = backupAllowed;
-        mDefaultToDeviceProtectedStorage = defaultToDeviceProtectedStorage;
-        mRequestLegacyExternalStorage = requestLegacyExternalStorage;
-        mUserDataFragile = userDataFragile;
-        mClearUserDataOnFailedRestoreAllowed = clearUserDataOnFailedRestoreAllowed;
+        mArchivedPackage = null;
     }
 
     public ApkLite(String path, ArchivedPackageParcel archivedPackage) {
@@ -257,16 +225,7 @@
         mRollbackDataPolicy = 0;
         mHasDeviceAdminReceiver = false;
         mIsSdkLibrary = false;
-        // @see ParsingPackageUtils#parseBaseAppBasicFlags
-        mBackupAllowed = XmlUtils.convertValueToBoolean(archivedPackage.backupAllowed, true);
-        mDefaultToDeviceProtectedStorage = XmlUtils.convertValueToBoolean(
-                archivedPackage.defaultToDeviceProtectedStorage, false);
-        mRequestLegacyExternalStorage = XmlUtils.convertValueToBoolean(
-                archivedPackage.requestLegacyExternalStorage,
-                mTargetSdkVersion < Build.VERSION_CODES.Q);
-        mUserDataFragile = XmlUtils.convertValueToBoolean(archivedPackage.userDataFragile, false);
-        mClearUserDataOnFailedRestoreAllowed = XmlUtils.convertValueToBoolean(
-                archivedPackage.clearUserDataOnFailedRestoreAllowed, true);
+        mArchivedPackage = archivedPackage;
     }
 
     /**
@@ -576,53 +535,18 @@
     }
 
     /**
-     *  Set to <code>false</code> if the application does not wish to permit any OS-driven
-     *  backups of its data; <code>true</code> otherwise.
+     * Archival install info.
      */
     @DataClass.Generated.Member
-    public boolean isBackupAllowed() {
-        return mBackupAllowed;
-    }
-
-    /**
-     * When set, the default data storage directory for this app is pointed at
-     * the device-protected location.
-     */
-    @DataClass.Generated.Member
-    public boolean isDefaultToDeviceProtectedStorage() {
-        return mDefaultToDeviceProtectedStorage;
-    }
-
-    /**
-     * If {@code true} this app requests full external storage access.
-     */
-    @DataClass.Generated.Member
-    public boolean isRequestLegacyExternalStorage() {
-        return mRequestLegacyExternalStorage;
-    }
-
-    /**
-     * Indicates whether this application has declared its user data as fragile, causing the
-     * system to prompt the user on whether to keep the user data on uninstall.
-     */
-    @DataClass.Generated.Member
-    public boolean isUserDataFragile() {
-        return mUserDataFragile;
-    }
-
-    /**
-     * Indicates whether this application's data will be cleared on a failed restore.
-     */
-    @DataClass.Generated.Member
-    public boolean isClearUserDataOnFailedRestoreAllowed() {
-        return mClearUserDataOnFailedRestoreAllowed;
+    public @Nullable ArchivedPackageParcel getArchivedPackage() {
+        return mArchivedPackage;
     }
 
     @DataClass.Generated(
-            time = 1693513509013L,
+            time = 1694792109463L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\nprivate final  boolean mHasDeviceAdminReceiver\nprivate final  boolean mIsSdkLibrary\nprivate final  boolean mBackupAllowed\nprivate final  boolean mDefaultToDeviceProtectedStorage\nprivate final  boolean mRequestLegacyExternalStorage\nprivate final  boolean mUserDataFragile\nprivate final  boolean mClearUserDataOnFailedRestoreAllowed\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\nprivate final  boolean mHasDeviceAdminReceiver\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 066ff689..5f86742 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -40,7 +40,6 @@
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
 
@@ -447,13 +446,6 @@
         int overlayPriority = 0;
         int rollbackDataPolicy = 0;
 
-        boolean clearUserDataAllowed = true;
-        boolean backupAllowed = true;
-        boolean defaultToDeviceProtectedStorage = false;
-        String requestLegacyExternalStorage = null;
-        boolean userDataFragile = false;
-        boolean clearUserDataOnFailedRestoreAllowed = true;
-
         String requiredSystemPropertyName = null;
         String requiredSystemPropertyValue = null;
 
@@ -493,22 +485,6 @@
                 useEmbeddedDex = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
                         "useEmbeddedDex", false);
 
-                clearUserDataAllowed = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
-                        "allowClearUserDataOnFailedRestore", true);
-                backupAllowed = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
-                        "allowBackup", true);
-                defaultToDeviceProtectedStorage = parser.getAttributeBooleanValue(
-                        ANDROID_RES_NAMESPACE,
-                        "defaultToDeviceProtectedStorage", false);
-                userDataFragile = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
-                        "hasFragileUserData", false);
-                clearUserDataOnFailedRestoreAllowed = parser.getAttributeBooleanValue(
-                        ANDROID_RES_NAMESPACE,
-                        "allowClearUserDataOnFailedRestore", true);
-
-                requestLegacyExternalStorage = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
-                        "requestLegacyExternalStorage");
-
                 rollbackDataPolicy = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
                         "rollbackDataPolicy", 0);
                 String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
@@ -629,9 +605,6 @@
             return input.skip(message);
         }
 
-        boolean isRequestLegacyExternalStorage = XmlUtils.convertValueToBoolean(
-                requestLegacyExternalStorage, targetSdkVersion < Build.VERSION_CODES.Q);
-
         return input.success(
                 new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                         configForSplit, usesSplitName, isSplitRequired, versionCode,
@@ -641,9 +614,7 @@
                         overlayIsStatic, overlayPriority, requiredSystemPropertyName,
                         requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
                         rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
-                        hasDeviceAdminReceiver, isSdkLibrary, clearUserDataAllowed, backupAllowed,
-                        defaultToDeviceProtectedStorage, isRequestLegacyExternalStorage,
-                        userDataFragile, clearUserDataOnFailedRestoreAllowed));
+                        hasDeviceAdminReceiver, isSdkLibrary));
     }
 
     private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index ccef9de..116dd1f 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.PackageInfo;
 import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
@@ -112,29 +113,11 @@
      * Indicates if this package is a sdk.
      */
     private final boolean mIsSdkLibrary;
+
     /**
-     *  Set to <code>false</code> if the application does not wish to permit any OS-driven
-     *  backups of its data; <code>true</code> otherwise.
+     * Archival install info.
      */
-    private final boolean mBackupAllowed;
-    /**
-     * When set, the default data storage directory for this app is pointed at
-     * the device-protected location.
-     */
-    private final boolean mDefaultToDeviceProtectedStorage;
-    /**
-     * If {@code true} this app requests full external storage access.
-     */
-    private final boolean mRequestLegacyExternalStorage;
-    /**
-     * Indicates whether this application has declared its user data as fragile, causing the
-     * system to prompt the user on whether to keep the user data on uninstall.
-     */
-    private final boolean mUserDataFragile;
-    /**
-     * Indicates whether this application's data will be cleared on a failed restore.
-     */
-    private final boolean mClearUserDataOnFailedRestoreAllowed;
+    private final @Nullable ArchivedPackageParcel mArchivedPackage;
 
     public PackageLite(String path, String baseApkPath, ApkLite baseApk,
             String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
@@ -171,11 +154,7 @@
         mSplitApkPaths = splitApkPaths;
         mSplitRevisionCodes = splitRevisionCodes;
         mTargetSdk = targetSdk;
-        mBackupAllowed = baseApk.isBackupAllowed();
-        mDefaultToDeviceProtectedStorage = baseApk.isDefaultToDeviceProtectedStorage();
-        mRequestLegacyExternalStorage = baseApk.isRequestLegacyExternalStorage();
-        mUserDataFragile = baseApk.isUserDataFragile();
-        mClearUserDataOnFailedRestoreAllowed = baseApk.isClearUserDataOnFailedRestoreAllowed();
+        mArchivedPackage = baseApk.getArchivedPackage();
     }
 
     /**
@@ -455,53 +434,18 @@
     }
 
     /**
-     *  Set to <code>false</code> if the application does not wish to permit any OS-driven
-     *  backups of its data; <code>true</code> otherwise.
+     * Archival install info.
      */
     @DataClass.Generated.Member
-    public boolean isBackupAllowed() {
-        return mBackupAllowed;
-    }
-
-    /**
-     * When set, the default data storage directory for this app is pointed at
-     * the device-protected location.
-     */
-    @DataClass.Generated.Member
-    public boolean isDefaultToDeviceProtectedStorage() {
-        return mDefaultToDeviceProtectedStorage;
-    }
-
-    /**
-     * If {@code true} this app requests full external storage access.
-     */
-    @DataClass.Generated.Member
-    public boolean isRequestLegacyExternalStorage() {
-        return mRequestLegacyExternalStorage;
-    }
-
-    /**
-     * Indicates whether this application has declared its user data as fragile, causing the
-     * system to prompt the user on whether to keep the user data on uninstall.
-     */
-    @DataClass.Generated.Member
-    public boolean isUserDataFragile() {
-        return mUserDataFragile;
-    }
-
-    /**
-     * Indicates whether this application's data will be cleared on a failed restore.
-     */
-    @DataClass.Generated.Member
-    public boolean isClearUserDataOnFailedRestoreAllowed() {
-        return mClearUserDataOnFailedRestoreAllowed;
+    public @Nullable ArchivedPackageParcel getArchivedPackage() {
+        return mArchivedPackage;
     }
 
     @DataClass.Generated(
-            time = 1693513525097L,
+            time = 1694792176268L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mTargetSdk\nprivate final  int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final  int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mProfileableByShell\nprivate final  boolean mUseEmbeddedDex\nprivate final  boolean mIsSdkLibrary\nprivate final  boolean mBackupAllowed\nprivate final  boolean mDefaultToDeviceProtectedStorage\nprivate final  boolean mRequestLegacyExternalStorage\nprivate final  boolean mUserDataFragile\nprivate final  boolean mClearUserDataOnFailedRestoreAllowed\npublic  java.util.List<java.lang.String> getAllApkPaths()\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mTargetSdk\nprivate final  int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final  int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mProfileableByShell\nprivate final  boolean mUseEmbeddedDex\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  java.util.List<java.lang.String> getAllApkPaths()\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/res/OWNERS b/core/java/android/content/res/OWNERS
index a7bce12..141d58d 100644
--- a/core/java/android/content/res/OWNERS
+++ b/core/java/android/content/res/OWNERS
@@ -1,8 +1,7 @@
 # Bug component: 568761
 
-toddke@android.com
-toddke@google.com
 patb@google.com
 zyy@google.com
+branliu@google.com
 
 per-file FontScaleConverter*=fuego@google.com
\ No newline at end of file
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index eedb25b..20771af 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.Hide;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -117,6 +118,52 @@
     }
 
     /**
+     * Returns a list of candidate credentials returned from credential manager providers
+     *
+     * @param request the request specifying type(s) of credentials to get from the
+     *                credential providers
+     * @param cancellationSignal an optional signal that allows for cancelling this call
+     * @param executor the callback will take place on this {@link Executor}
+     * @param callback the callback invoked when the request succeeds or fails
+     *
+     * @hide
+     */
+    @Hide
+    public void getCandidateCredentials(
+            @NonNull GetCredentialRequest request,
+            @NonNull String callingPackage,
+            @Nullable CancellationSignal cancellationSignal,
+            @CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
+                    GetCandidateCredentialsException> callback
+    ) {
+        requireNonNull(request, "request must not be null");
+        requireNonNull(callingPackage, "callingPackage must not be null");
+        requireNonNull(executor, "executor must not be null");
+        requireNonNull(callback, "callback must not be null");
+
+        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+            Log.w(TAG, "getCandidateCredentials already canceled");
+            return;
+        }
+
+        ICancellationSignal cancelRemote = null;
+        try {
+            cancelRemote =
+                    mService.getCandidateCredentials(
+                            request,
+                            new GetCandidateCredentialsTransport(executor, callback),
+                            mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+
+        if (cancellationSignal != null && cancelRemote != null) {
+            cancellationSignal.setRemote(cancelRemote);
+        }
+    }
+
+    /**
      * Launches the necessary flows to retrieve an app credential from the user.
      *
      * <p>The execution can potentially launch UI flows to collect user consent to using a
@@ -641,6 +688,44 @@
         }
     }
 
+    private static class GetCandidateCredentialsTransport
+            extends IGetCandidateCredentialsCallback.Stub {
+
+        private final Executor mExecutor;
+        private final OutcomeReceiver<GetCandidateCredentialsResponse,
+                GetCandidateCredentialsException> mCallback;
+
+        private GetCandidateCredentialsTransport(
+                Executor executor,
+                OutcomeReceiver<GetCandidateCredentialsResponse,
+                        GetCandidateCredentialsException> callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onResponse(GetCandidateCredentialsResponse response) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onResult(response));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void onError(String errorType, String message) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(
+                        () -> mCallback.onError(new GetCandidateCredentialsException(
+                                errorType, message)));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
     private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
         // TODO: listen for cancellation to release callback.
 
diff --git a/core/java/android/credentials/GetCandidateCredentialsException.java b/core/java/android/credentials/GetCandidateCredentialsException.java
new file mode 100644
index 0000000..40650d0
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsException.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Represents an error encountered during the
+ * {@link CredentialManager#getCandidateCredentials} operation.
+ *
+ * @hide
+ */
+@Hide
+public class GetCandidateCredentialsException extends Exception {
+    /**
+     * The error type value for when the given operation failed due to an unknown reason.
+     */
+    @NonNull
+    public static final String TYPE_UNKNOWN =
+            "android.credentials.GetCandidateCredentialsException.TYPE_UNKNOWN";
+
+    /**
+     * The error type value for when no credential is found available for the given {@link
+     * CredentialManager#getCandidateCredentials} request.
+     */
+    @NonNull
+    public static final String TYPE_NO_CREDENTIAL =
+            "android.credentials.GetCandidateCredentialsException.TYPE_NO_CREDENTIAL";
+
+    @NonNull
+    private final String mType;
+
+    /** Returns the specific exception type. */
+    @NonNull
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Constructs a {@link GetCandidateCredentialsException}.
+     *
+     * @throws IllegalArgumentException If type is empty.
+     */
+    public GetCandidateCredentialsException(@NonNull String type, @Nullable String message) {
+        this(type, message, null);
+    }
+
+    /**
+     * Constructs a {@link GetCandidateCredentialsException}.
+     *
+     * @throws IllegalArgumentException If type is empty.
+     */
+    public GetCandidateCredentialsException(
+            @NonNull String type, @Nullable String message, @Nullable Throwable cause) {
+        super(message, cause);
+        this.mType = Preconditions.checkStringNotEmpty(type,
+                "type must not be empty");
+    }
+
+    /**
+     * Constructs a {@link GetCandidateCredentialsException}.
+     *
+     * @throws IllegalArgumentException If type is empty.
+     */
+    public GetCandidateCredentialsException(@NonNull String type, @Nullable Throwable cause) {
+        this(type, null, cause);
+    }
+
+    /**
+     * Constructs a {@link GetCandidateCredentialsException}.
+     *
+     * @throws IllegalArgumentException If type is empty.
+     */
+    public GetCandidateCredentialsException(@NonNull String type) {
+        this(type, null, null);
+    }
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsRequest.aidl b/core/java/android/credentials/GetCandidateCredentialsRequest.aidl
new file mode 100644
index 0000000..d361089
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 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.credentials;
+
+parcelable GetCandidateCredentialsRequest;
\ No newline at end of file
diff --git a/core/java/android/credentials/GetCandidateCredentialsRequest.java b/core/java/android/credentials/GetCandidateCredentialsRequest.java
new file mode 100644
index 0000000..7f0dcaf
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsRequest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A request to retrieve a list of candidate credentials against the list of credential
+ * options
+ *
+ * @hide
+ */
+@Hide
+public final class GetCandidateCredentialsRequest implements Parcelable {
+
+    /**
+     * The list of credential requests.
+     */
+    @NonNull
+    private final List<CredentialOption> mCredentialOptions;
+
+    /**
+     * The top request level data.
+     */
+    @NonNull
+    private final Bundle mData;
+
+    /**
+     * The origin of the calling app. Callers of this special API (e.g. browsers)
+     * can set this origin for an app different from their own, to be able to get credentials
+     * on behalf of that app.
+     */
+    @Nullable
+    private String mOrigin;
+
+    /**
+     * Returns the list of credential options to be requested.
+     */
+    @NonNull
+    public List<CredentialOption> getCredentialOptions() {
+        return mCredentialOptions;
+    }
+
+    /**
+     * Returns the top request level data.
+     */
+    @NonNull
+    public Bundle getData() {
+        return mData;
+    }
+
+    /**
+     * Returns the origin of the calling app if set otherwise returns null.
+     */
+    @Nullable
+    public String getOrigin() {
+        return mOrigin;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedList(mCredentialOptions, flags);
+        dest.writeBundle(mData);
+        dest.writeString8(mOrigin);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "GetCandidateCredentialsRequest {credentialOption=" + mCredentialOptions
+                + ", data=" + mData
+                + ", origin=" + mOrigin
+                + "}";
+    }
+
+    private GetCandidateCredentialsRequest(@NonNull List<CredentialOption> credentialOptions,
+            @NonNull Bundle data, String origin) {
+        Preconditions.checkCollectionNotEmpty(
+                credentialOptions,
+                /*valueName=*/ "credentialOptions");
+        Preconditions.checkCollectionElementsNotNull(
+                credentialOptions,
+                /*valueName=*/ "credentialOptions");
+        mCredentialOptions = credentialOptions;
+        mData = requireNonNull(data,
+                "data must not be null");
+        mOrigin = origin;
+    }
+
+    private GetCandidateCredentialsRequest(@NonNull Parcel in) {
+        List<CredentialOption> credentialOptions = new ArrayList<CredentialOption>();
+        in.readTypedList(credentialOptions, CredentialOption.CREATOR);
+        mCredentialOptions = credentialOptions;
+        AnnotationValidations.validate(NonNull.class, null, mCredentialOptions);
+
+        Bundle data = in.readBundle();
+        mData = data;
+        AnnotationValidations.validate(NonNull.class, null, mData);
+
+        mOrigin = in.readString8();
+    }
+
+    @NonNull
+    public static final Creator<GetCandidateCredentialsRequest> CREATOR =
+            new Creator<>() {
+                @Override
+                public GetCandidateCredentialsRequest[] newArray(int size) {
+                    return new GetCandidateCredentialsRequest[size];
+                }
+
+                @Override
+                public GetCandidateCredentialsRequest createFromParcel(@NonNull Parcel in) {
+                    return new GetCandidateCredentialsRequest(in);
+                }
+            };
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.aidl b/core/java/android/credentials/GetCandidateCredentialsResponse.aidl
new file mode 100644
index 0000000..ffcd3e7
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 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.credentials;
+
+parcelable GetCandidateCredentialsResponse;
\ No newline at end of file
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
new file mode 100644
index 0000000..231e4bc
--- /dev/null
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.credentials.ui.GetCredentialProviderData;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of candidate credentials.
+ *
+ * @hide
+ */
+@Hide
+public final class GetCandidateCredentialsResponse implements Parcelable {
+    // TODO(b/299321990): Add members
+
+    @NonNull
+    private final List<GetCredentialProviderData> mCandidateProviderDataList;
+
+    /**
+     * @hide
+     */
+    @Hide
+    public GetCandidateCredentialsResponse(
+            List<GetCredentialProviderData> candidateProviderDataList
+    ) {
+        Preconditions.checkCollectionNotEmpty(
+                candidateProviderDataList,
+                /*valueName=*/ "candidateProviderDataList");
+        mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
+    }
+
+    protected GetCandidateCredentialsResponse(Parcel in) {
+        List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
+        in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
+        mCandidateProviderDataList = candidateProviderDataList;
+
+        AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedList(mCandidateProviderDataList);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<GetCandidateCredentialsResponse> CREATOR =
+            new Creator<>() {
+                @Override
+                public GetCandidateCredentialsResponse createFromParcel(Parcel in) {
+                    return new GetCandidateCredentialsResponse(in);
+                }
+
+                @Override
+                public GetCandidateCredentialsResponse[] newArray(int size) {
+                    return new GetCandidateCredentialsResponse[size];
+                }
+            };
+}
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index dec729f..d081576 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -21,12 +21,14 @@
 import android.credentials.CredentialProviderInfo;
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialRequest;
+import android.credentials.GetCandidateCredentialsRequest;
 import android.credentials.GetCredentialRequest;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
 import android.credentials.IClearCredentialStateCallback;
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.IGetCredentialCallback;
+import android.credentials.IGetCandidateCredentialsCallback;
 import android.credentials.IPrepareGetCredentialCallback;
 import android.credentials.ISetEnabledProvidersCallback;
 import android.content.ComponentName;
@@ -45,6 +47,8 @@
 
     @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
 
+    @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, String callingPackage);
+
     @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
 
     void setEnabledProviders(in List<String> primaryProviders, in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
diff --git a/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl b/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl
new file mode 100644
index 0000000..729176a
--- /dev/null
+++ b/core/java/android/credentials/IGetCandidateCredentialsCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 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.credentials;
+
+import android.app.PendingIntent;
+import android.credentials.GetCandidateCredentialsResponse;
+
+/**
+ * Listener for a getCandidateCredentials request.
+ *
+ * @hide
+ */
+interface IGetCandidateCredentialsCallback {
+    oneway void onResponse(in GetCandidateCredentialsResponse response);
+    oneway void onError(String errorType, String message);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
index 1c2cbbc..b1d6ac4 100644
--- a/core/java/android/hardware/CameraSessionStats.java
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -64,6 +64,7 @@
     private ArrayList<CameraStreamStats> mStreamStats;
     private String mUserTag;
     private int mVideoStabilizationMode;
+    private boolean mUsedUltraWide;
     private int mSessionIndex;
     private CameraExtensionSessionStats mCameraExtensionSessionStats;
 
@@ -82,6 +83,7 @@
         mDeviceError = false;
         mStreamStats = new ArrayList<CameraStreamStats>();
         mVideoStabilizationMode = -1;
+        mUsedUltraWide = false;
         mSessionIndex = 0;
         mCameraExtensionSessionStats = new CameraExtensionSessionStats();
     }
@@ -102,6 +104,8 @@
         mSessionType = sessionType;
         mInternalReconfigure = internalReconfigure;
         mStreamStats = new ArrayList<CameraStreamStats>();
+        mVideoStabilizationMode = -1;
+        mUsedUltraWide = false;
         mSessionIndex = sessionIdx;
         mCameraExtensionSessionStats = new CameraExtensionSessionStats();
     }
@@ -147,6 +151,7 @@
         dest.writeTypedList(mStreamStats);
         dest.writeString(mUserTag);
         dest.writeInt(mVideoStabilizationMode);
+        dest.writeBoolean(mUsedUltraWide);
         dest.writeInt(mSessionIndex);
         mCameraExtensionSessionStats.writeToParcel(dest, 0);
     }
@@ -173,6 +178,9 @@
 
         mUserTag = in.readString();
         mVideoStabilizationMode = in.readInt();
+
+        mUsedUltraWide = in.readBoolean();
+
         mSessionIndex = in.readInt();
         mCameraExtensionSessionStats = CameraExtensionSessionStats.CREATOR.createFromParcel(in);
     }
@@ -245,6 +253,10 @@
         return mVideoStabilizationMode;
     }
 
+    public boolean getUsedUltraWide() {
+        return mUsedUltraWide;
+    }
+
     public int getSessionIndex() {
         return mSessionIndex;
     }
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4700720..b1aa7de 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -32,7 +32,6 @@
 import android.view.SurfaceControl.RefreshRateRange;
 import android.view.SurfaceControl.Transaction;
 import android.window.DisplayWindowPolicyController;
-import android.window.ScreenCapture;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -112,25 +111,6 @@
     public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener);
 
     /**
-     * Screenshot for internal system-only use such as rotation, etc.  This method includes
-     * secure layers and the result should never be exposed to non-system applications.
-     * This method does not apply any rotation and provides the output in natural orientation.
-     *
-     * @param displayId The display id to take the screenshot of.
-     * @return The buffer or null if we have failed.
-     */
-    public abstract ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId);
-
-    /**
-     * General screenshot functionality that excludes secure layers and applies appropriate
-     * rotation that the device is currently in.
-     *
-     * @param displayId The display id to take the screenshot of.
-     * @return The buffer or null if we have failed.
-     */
-    public abstract ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId);
-
-    /**
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c3fae55..88d7231 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -41,6 +41,7 @@
 import android.view.InputEvent;
 import android.view.InputMonitor;
 import android.view.PointerIcon;
+import android.view.KeyCharacterMap;
 import android.view.VerifiedInputEvent;
 
 /** @hide */
@@ -63,6 +64,8 @@
     // active keyboard layout.
     int getKeyCodeForKeyLocation(int deviceId, in int locationKeyCode);
 
+    KeyCharacterMap getKeyCharacterMap(String layoutDescriptor);
+
     // Temporarily changes the pointer speed.
     void tryPointerSpeed(int speed);
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a0cceae..ff1a6ac 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.input;
 
+import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
+
 import android.Manifest;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
@@ -31,6 +33,7 @@
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.hardware.BatteryState;
 import android.os.Build;
 import android.os.Handler;
@@ -931,6 +934,31 @@
     }
 
     /**
+     * Provides a Keyboard layout preview of a particular dimension.
+     *
+     * @param keyboardLayout Layout whose preview is requested. If null, will return preview of
+     *                       the default Keyboard layout defined by {@code Generic.kl}.
+     * @param width Expected width of the drawable
+     * @param height Expected height of the drawable
+     *
+     * NOTE: Width and height will auto-adjust to the width and height of the ImageView that
+     * shows the drawable but this allows the caller to provide an intrinsic width and height of
+     * the drawable allowing the ImageView to properly wrap the drawable content.
+     *
+     * @hide
+     */
+    @Nullable
+    public Drawable getKeyboardLayoutPreview(@Nullable KeyboardLayout keyboardLayout, int width,
+            int height) {
+        if (!keyboardLayoutPreviewFlag()) {
+            return null;
+        }
+        PhysicalKeyLayout keyLayout = new PhysicalKeyLayout(
+                mGlobal.getKeyCharacterMap(keyboardLayout), keyboardLayout);
+        return new KeyboardLayoutPreviewDrawable(mContext, keyLayout, width, height);
+    }
+
+    /**
      * Injects an input event into the event system, targeting windows owned by the provided uid.
      *
      * If a valid targetUid is provided, the system will only consider injecting the input event
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index e886f68..8c598ae 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -51,6 +51,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
 import android.view.PointerIcon;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1206,6 +1207,21 @@
     }
 
     /**
+     * Returns KeyCharacterMap for the provided Keyboard layout. If provided layout is null it will
+     * return KeyCharacter map for the default layout {@code Generic.kl}.
+     */
+    public KeyCharacterMap getKeyCharacterMap(@Nullable KeyboardLayout keyboardLayout) {
+        if (keyboardLayout == null) {
+            return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+        }
+        try {
+            return mIm.getKeyCharacterMap(keyboardLayout.getDescriptor());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @see InputManager#injectInputEvent(InputEvent, int, int)
      */
 
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 4403251..bbfed24 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -22,6 +22,7 @@
 import android.os.Parcelable;
 
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 
@@ -230,6 +231,33 @@
         return mProductId;
     }
 
+    /**
+     * Returns if the Keyboard layout follows the ANSI Physical key layout.
+     */
+    public boolean isAnsiLayout() {
+        for (int i = 0; i < mLocales.size(); i++) {
+            Locale locale = mLocales.get(i);
+            if (locale != null && locale.getCountry().equalsIgnoreCase("us")
+                    && mLayoutType != LayoutType.EXTENDED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns if the Keyboard layout follows the JIS Physical key layout.
+     */
+    public boolean isJisLayout() {
+        for (int i = 0; i < mLocales.size(); i++) {
+            Locale locale = mLocales.get(i);
+            if (locale != null && locale.getCountry().equalsIgnoreCase("jp")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
new file mode 100644
index 0000000..d943c37
--- /dev/null
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -0,0 +1,504 @@
+/*
+ * 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 android.hardware.input;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom drawable class that draws preview of a Physical keyboard layout.
+ */
+final class KeyboardLayoutPreviewDrawable extends Drawable {
+
+    private static final String TAG = "KeyboardLayoutPreview";
+    private static final int GRAVITY_LEFT = 0x1;
+    private static final int GRAVITY_RIGHT = 0x2;
+    private static final int GRAVITY_TOP = 0x4;
+    private static final int GRAVITY_BOTTOM = 0x8;
+    private static final int GRAVITY_CENTER =
+            GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
+    private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+    private static final int KEY_PADDING_IN_DP = 3;
+    private static final int KEYBOARD_PADDING_IN_DP = 10;
+    private static final int KEY_RADIUS_IN_DP = 5;
+    private static final int KEYBOARD_RADIUS_IN_DP = 10;
+    private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+
+    private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
+
+    private final int mWidth;
+    private final int mHeight;
+    private final RectF mKeyboardBackground = new RectF();
+    private final ResourceProvider mResourceProvider;
+    private final PhysicalKeyLayout mKeyLayout;
+
+    public KeyboardLayoutPreviewDrawable(Context context, PhysicalKeyLayout keyLayout, int width,
+            int height) {
+        mWidth = width;
+        mHeight = height;
+        mResourceProvider = new ResourceProvider(context);
+        mKeyLayout = keyLayout;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mHeight;
+    }
+
+    @Override
+    protected void onBoundsChange(@NonNull Rect bounds) {
+        super.onBoundsChange(bounds);
+        mKeyDrawables.clear();
+        final PhysicalKeyLayout.LayoutKey[][] keys = mKeyLayout.getKeys();
+        if (keys == null) {
+            return;
+        }
+        final PhysicalKeyLayout.EnterKey enterKey = mKeyLayout.getEnterKey();
+        int width = bounds.width();
+        int height = bounds.height();
+        final int keyboardPadding = mResourceProvider.getKeyboardPadding();
+        final int keyPadding = mResourceProvider.getKeyPadding();
+        final float keyRadius = mResourceProvider.getKeyRadius();
+        mKeyboardBackground.set(0, 0, width, height);
+        width -= keyboardPadding * 2;
+        height -= keyboardPadding * 2;
+        if (width <= 0 || height <= 0) {
+            Slog.e(TAG, "Invalid width and height to draw layout preview, width = " + width
+                    + ", height = " + height);
+            return;
+        }
+        int rowCount = keys.length;
+        float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+        float isoEnterKeyLeft = 0;
+        float isoEnterKeyTop = 0;
+        float isoEnterWidthUnit = 0;
+        for (int i = 0; i < rowCount; i++) {
+            PhysicalKeyLayout.LayoutKey[] row = keys[i];
+            float totalRowWeight = 0;
+            int keysInRow = row.length;
+            for (PhysicalKeyLayout.LayoutKey layoutKey : row) {
+                totalRowWeight += layoutKey.keyWeight();
+            }
+            float keyWidthInPx = (width - keysInRow * 2 * keyPadding) / totalRowWeight;
+            float rowWeightOnLeft = 0;
+            float top = keyboardPadding + keyPadding * (2 * i + 1) + i * keyHeight;
+            for (int j = 0; j < keysInRow; j++) {
+                float left =
+                        keyboardPadding + keyPadding * (2 * j + 1) + rowWeightOnLeft * keyWidthInPx;
+                rowWeightOnLeft += row[j].keyWeight();
+                RectF keyRect = new RectF(left, top, left + keyWidthInPx * row[j].keyWeight(),
+                        top + keyHeight);
+                if (enterKey != null && row[j].keyCode() == KeyEvent.KEYCODE_ENTER) {
+                    if (enterKey.row() == i && enterKey.column() == j) {
+                        isoEnterKeyLeft = keyRect.left;
+                        isoEnterKeyTop = keyRect.top;
+                        isoEnterWidthUnit = keyWidthInPx;
+                    }
+                    continue;
+                }
+                if (PhysicalKeyLayout.isSpecialKey(row[j])) {
+                    mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+                            mResourceProvider.getSpecialKeyPaint(),
+                            mResourceProvider.getSpecialKeyPaint(),
+                            mResourceProvider.getSpecialKeyPaint()));
+                } else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
+                    mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
+                            keyRadius, mResourceProvider.getTypingKeyPaint(),
+                            mResourceProvider.getPrimaryGlyphPaint(),
+                            mResourceProvider.getSecondaryGlyphPaint()));
+                } else {
+                    mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+                            mResourceProvider.getTypingKeyPaint(),
+                            mResourceProvider.getPrimaryGlyphPaint(),
+                            mResourceProvider.getSecondaryGlyphPaint()));
+                }
+            }
+        }
+        if (enterKey != null) {
+            IsoEnterKey.Builder isoEnterKeyBuilder = new IsoEnterKey.Builder(keyRadius,
+                    mResourceProvider.getSpecialKeyPaint());
+            isoEnterKeyBuilder.setTopWidth(enterKey.topKeyWeight() * isoEnterWidthUnit)
+                    .setStartPoint(isoEnterKeyLeft, isoEnterKeyTop)
+                    .setVerticalEdges(keyHeight, 2 * (keyHeight + keyPadding))
+                    .setBottomWidth(enterKey.bottomKeyWeight() * isoEnterWidthUnit);
+            mKeyDrawables.add(isoEnterKeyBuilder.build());
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final float keyboardRadius = mResourceProvider.getBackgroundRadius();
+        canvas.drawRoundRect(mKeyboardBackground, keyboardRadius, keyboardRadius,
+                mResourceProvider.getBackgroundPaint());
+        for (KeyDrawable key : mKeyDrawables) {
+            key.draw(canvas);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        // Do nothing
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        // Do nothing
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+
+    private static class TypingKey implements KeyDrawable {
+
+        private final RectF mKeyRect;
+        private final float mKeyRadius;
+        private final Paint mKeyPaint;
+        private final Paint mBaseTextPaint;
+        private final Paint mModifierTextPaint;
+        private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
+
+        private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
+                float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+            mKeyRect = keyRect;
+            mKeyRadius = keyRadius;
+            mKeyPaint = keyPaint;
+            mBaseTextPaint = baseTextPaint;
+            mModifierTextPaint = modifierTextPaint;
+            initGlyphs(glyphData);
+        }
+
+        private void initGlyphs(@Nullable PhysicalKeyLayout.KeyGlyph glyphData) {
+            createGlyphs(glyphData);
+            measureGlyphs();
+        }
+
+        private void createGlyphs(@Nullable PhysicalKeyLayout.KeyGlyph glyphData) {
+            if (glyphData == null) {
+                return;
+            }
+            if (!glyphData.hasBaseText()) {
+                return;
+            }
+            if (glyphData.hasValidShiftText() && glyphData.hasValidAltGrText()) {
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+                        GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
+                        GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
+                        GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
+            } else if (glyphData.hasValidShiftText()) {
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+                        GRAVITY_BOTTOM | GRAVITY_CENTER_HORIZONTAL, mBaseTextPaint));
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
+                        GRAVITY_TOP | GRAVITY_CENTER_HORIZONTAL, mModifierTextPaint));
+            } else if (glyphData.hasValidAltGrText()) {
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+                        GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
+                        GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
+            } else {
+                mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
+                        GRAVITY_CENTER, mBaseTextPaint));
+            }
+        }
+
+        private void measureGlyphs() {
+            float keyWidth = mKeyRect.width();
+            float keyHeight = mKeyRect.height();
+            for (GlyphDrawable glyph : mGlyphDrawables) {
+                float centerX = keyWidth / 2;
+                float centerY = keyHeight / 2;
+                if ((glyph.gravity & GRAVITY_LEFT) != 0) {
+                    centerX -= keyWidth / 4;
+                }
+                if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
+                    centerX += keyWidth / 4;
+                }
+                if ((glyph.gravity & GRAVITY_TOP) != 0) {
+                    centerY -= keyHeight / 4;
+                }
+                if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
+                    centerY += keyHeight / 4;
+                }
+                Rect textBounds = new Rect();
+                glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
+                float textWidth = textBounds.width();
+                float textHeight = textBounds.height();
+                glyph.rect.set(centerX - textWidth / 2, centerY - textHeight / 2 - textBounds.top,
+                        centerX + textWidth / 2, centerY + textHeight / 2 - textBounds.top);
+            }
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            canvas.drawRoundRect(mKeyRect, mKeyRadius, mKeyRadius, mKeyPaint);
+            for (GlyphDrawable glyph : mGlyphDrawables) {
+                float textWidth = glyph.rect.width();
+                float textHeight = glyph.rect.height();
+                float keyWidth = mKeyRect.width();
+                float keyHeight = mKeyRect.height();
+                if (textWidth == 0 || textHeight == 0 || keyWidth == 0 || keyHeight == 0) {
+                    return;
+                }
+                canvas.drawText(glyph.text, 0, glyph.text.length(), mKeyRect.left + glyph.rect.left,
+                        mKeyRect.top + glyph.rect.top, glyph.paint);
+            }
+        }
+    }
+
+    private static class UnsureTypingKey extends TypingKey {
+
+        private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
+                RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
+                Paint modifierTextPaint) {
+            super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+                    createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
+        }
+    }
+
+    private static class IsoEnterKey implements KeyDrawable {
+
+        private final Paint mKeyPaint;
+        private final Path mPath;
+
+        private IsoEnterKey(Paint keyPaint, @NonNull Path path) {
+            mKeyPaint = keyPaint;
+            mPath = path;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            canvas.drawPath(mPath, mKeyPaint);
+        }
+
+        private static class Builder {
+            private final float mKeyRadius;
+            private final Paint mKeyPaint;
+            private float mLeft;
+            private float mTop;
+            private float mTopWidth;
+            private float mBottomWidth;
+            private float mLeftHeight;
+            private float mRightHeight;
+
+            private Builder(float keyRadius, Paint keyPaint) {
+                mKeyRadius = keyRadius;
+                mKeyPaint = keyPaint;
+            }
+
+            private Builder setStartPoint(float left, float top) {
+                mLeft = left;
+                mTop = top;
+                return this;
+            }
+
+            private Builder setTopWidth(float width) {
+                mTopWidth = width;
+                return this;
+            }
+
+            private Builder setBottomWidth(float width) {
+                mBottomWidth = width;
+                return this;
+            }
+
+            private Builder setVerticalEdges(float leftHeight, float rightHeight) {
+                mLeftHeight = leftHeight;
+                mRightHeight = rightHeight;
+                return this;
+            }
+
+            private IsoEnterKey build() {
+                Path enterKey = new Path();
+                RectF oval = new RectF(-mKeyRadius, -mKeyRadius, mKeyRadius, mKeyRadius);
+                // Horizontal top line
+                enterKey.moveTo(mLeft + mKeyRadius, mTop);
+                enterKey.lineTo(mLeft + mTopWidth - mKeyRadius, mTop);
+                // Rounded top right corner
+                oval.offsetTo(mLeft + mTopWidth - 2 * mKeyRadius, mTop);
+                enterKey.arcTo(oval, 270, 90);
+                // Vertical right line
+                enterKey.lineTo(mLeft + mTopWidth, mTop + mRightHeight - mKeyRadius);
+                // Rounded bottom right corner
+                oval.offsetTo(mLeft + mTopWidth - 2 * mKeyRadius,
+                        mTop + mRightHeight - 2 * mKeyRadius);
+                enterKey.arcTo(oval, 0, 90);
+                // Horizontal bottom line
+                enterKey.lineTo(mLeft + mTopWidth - mBottomWidth + mKeyRadius, mTop + mRightHeight);
+                // Rounded bottom left corner
+                oval.offsetTo(mLeft + mTopWidth - mBottomWidth,
+                        mTop + mRightHeight - 2 * mKeyRadius);
+                enterKey.arcTo(oval, 90, 90);
+                // Vertical left line (bottom half)
+                enterKey.lineTo(mLeft + mTopWidth - mBottomWidth, mTop + mLeftHeight - mKeyRadius);
+                // Rounded corner
+                oval.offsetTo(mLeft + mTopWidth - mBottomWidth - 2 * mKeyRadius,
+                        mTop + mLeftHeight);
+                enterKey.arcTo(oval, 0, -90);
+                // Horizontal line in the mid part
+                enterKey.lineTo(mLeft + mKeyRadius, mTop + mLeftHeight);
+                // Rounded corner
+                oval.offsetTo(mLeft, mTop + mLeftHeight - 2 * mKeyRadius);
+                enterKey.arcTo(oval, 90, 90);
+                // Vertical left line (top half)
+                enterKey.lineTo(mLeft, mTop + mKeyRadius);
+                // Rounded top left corner
+                oval.offsetTo(mLeft, mTop);
+                enterKey.arcTo(oval, 180, 90);
+                enterKey.close();
+                return new IsoEnterKey(mKeyPaint, enterKey);
+            }
+        }
+    }
+
+    private record GlyphDrawable(String text, RectF rect, int gravity, Paint paint) {}
+
+    private interface KeyDrawable {
+        void draw(Canvas canvas);
+    }
+
+    private static class ResourceProvider {
+        // Resources
+        private final Paint mBackgroundPaint;
+        private final Paint mTypingKeyPaint;
+        private final Paint mSpecialKeyPaint;
+        private final Paint mPrimaryGlyphPaint;
+        private final Paint mSecondaryGlyphPaint;
+        private final int mKeyPadding;
+        private final int mKeyboardPadding;
+        private final float mKeyRadius;
+        private final float mBackgroundRadius;
+
+        private ResourceProvider(Context context) {
+            mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    KEY_PADDING_IN_DP, context.getResources().getDisplayMetrics());
+            mKeyboardPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    KEYBOARD_PADDING_IN_DP, context.getResources().getDisplayMetrics());
+            mKeyRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
+            mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
+            int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                    GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+            boolean isDark = (context.getResources().getConfiguration().uiMode
+                    & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+            int typingKeyColor = context.getColor(
+                    isDark ? android.R.color.system_outline_variant_dark
+                            : android.R.color.system_surface_container_lowest_light);
+            int specialKeyColor = context.getColor(isDark ? android.R.color.system_neutral1_800
+                    : android.R.color.system_secondary_container_light);
+            int primaryGlyphColor = context.getColor(isDark ? android.R.color.system_on_surface_dark
+                    : android.R.color.system_on_surface_light);
+            int secondaryGlyphColor = context.getColor(isDark ? android.R.color.system_outline_dark
+                    : android.R.color.system_outline_light);
+            int backgroundColor = context.getColor(
+                    isDark ? android.R.color.system_surface_container_dark
+                            : android.R.color.system_surface_container_light);
+            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+                    Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+                    Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+            mTypingKeyPaint = createFillPaint(typingKeyColor);
+            mSpecialKeyPaint = createFillPaint(specialKeyColor);
+            mBackgroundPaint = createFillPaint(backgroundColor);
+        }
+
+        private Paint getBackgroundPaint() {
+            return mBackgroundPaint;
+        }
+
+        private Paint getTypingKeyPaint() {
+            return mTypingKeyPaint;
+        }
+
+        private Paint getSpecialKeyPaint() {
+            return mSpecialKeyPaint;
+        }
+
+        private Paint getPrimaryGlyphPaint() {
+            return mPrimaryGlyphPaint;
+        }
+
+        private Paint getSecondaryGlyphPaint() {
+            return mSecondaryGlyphPaint;
+        }
+
+        private int getKeyPadding() {
+            return mKeyPadding;
+        }
+
+        private int getKeyboardPadding() {
+            return mKeyboardPadding;
+        }
+
+        private float getKeyRadius() {
+            return mKeyRadius;
+        }
+
+        private float getBackgroundRadius() {
+            return mBackgroundRadius;
+        }
+    }
+
+    private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+        Paint paint = new Paint();
+        paint.setColor(textColor);
+        paint.setStyle(Paint.Style.FILL);
+        paint.setTextSize(textSize);
+        paint.setTypeface(typeface);
+        return paint;
+    }
+
+    private static Paint createFillPaint(@ColorInt int color) {
+        Paint paint = new Paint();
+        paint.setColor(color);
+        paint.setStyle(Paint.Style.FILL);
+        return paint;
+    }
+
+    private static Paint createGreyedOutPaint(Paint paint) {
+        Paint result = new Paint(paint);
+        result.setAlpha(100);
+        return result;
+    }
+}
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
new file mode 100644
index 0000000..241c452
--- /dev/null
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -0,0 +1,434 @@
+/*
+ * 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 android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.SparseIntArray;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+
+import java.util.Locale;
+
+/**
+ * A complimentary class to {@link KeyboardLayoutPreviewDrawable} describing the physical key layout
+ * of a Physical keyboard and provides information regarding the scan codes produced by the physical
+ * keys.
+ */
+final class PhysicalKeyLayout {
+
+    private static final String TAG = "KeyboardLayoutPreview";
+    private static final int SCANCODE_1 = 2;
+    private static final int SCANCODE_2 = 3;
+    private static final int SCANCODE_3 = 4;
+    private static final int SCANCODE_4 = 5;
+    private static final int SCANCODE_5 = 6;
+    private static final int SCANCODE_6 = 7;
+    private static final int SCANCODE_7 = 8;
+    private static final int SCANCODE_8 = 9;
+    private static final int SCANCODE_9 = 10;
+    private static final int SCANCODE_0 = 11;
+    private static final int SCANCODE_MINUS = 12;
+    private static final int SCANCODE_EQUALS = 13;
+    private static final int SCANCODE_Q = 16;
+    private static final int SCANCODE_W = 17;
+    private static final int SCANCODE_E = 18;
+    private static final int SCANCODE_R = 19;
+    private static final int SCANCODE_T = 20;
+    private static final int SCANCODE_Y = 21;
+    private static final int SCANCODE_U = 22;
+    private static final int SCANCODE_I = 23;
+    private static final int SCANCODE_O = 24;
+    private static final int SCANCODE_P = 25;
+    private static final int SCANCODE_LEFT_BRACKET = 26;
+    private static final int SCANCODE_RIGHT_BRACKET = 27;
+    private static final int SCANCODE_A = 30;
+    private static final int SCANCODE_S = 31;
+    private static final int SCANCODE_D = 32;
+    private static final int SCANCODE_F = 33;
+    private static final int SCANCODE_G = 34;
+    private static final int SCANCODE_H = 35;
+    private static final int SCANCODE_J = 36;
+    private static final int SCANCODE_K = 37;
+    private static final int SCANCODE_L = 38;
+    private static final int SCANCODE_SEMICOLON = 39;
+    private static final int SCANCODE_APOSTROPHE = 40;
+    private static final int SCANCODE_GRAVE = 41;
+    private static final int SCANCODE_BACKSLASH1 = 43;
+    private static final int SCANCODE_Z = 44;
+    private static final int SCANCODE_X = 45;
+    private static final int SCANCODE_C = 46;
+    private static final int SCANCODE_V = 47;
+    private static final int SCANCODE_B = 48;
+    private static final int SCANCODE_N = 49;
+    private static final int SCANCODE_M = 50;
+    private static final int SCANCODE_COMMA = 51;
+    private static final int SCANCODE_PERIOD = 52;
+    private static final int SCANCODE_SLASH = 53;
+    private static final int SCANCODE_BACKSLASH2 = 86;
+    private static final int SCANCODE_YEN = 124;
+
+    private static final SparseIntArray DEFAULT_KEYCODE_FOR_SCANCODE = new SparseIntArray();
+
+    static {
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_1, KeyEvent.KEYCODE_1);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_2, KeyEvent.KEYCODE_2);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_3, KeyEvent.KEYCODE_3);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_4, KeyEvent.KEYCODE_4);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_5, KeyEvent.KEYCODE_5);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_6, KeyEvent.KEYCODE_6);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_7, KeyEvent.KEYCODE_7);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_8, KeyEvent.KEYCODE_8);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_9, KeyEvent.KEYCODE_9);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_0, KeyEvent.KEYCODE_0);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_MINUS, KeyEvent.KEYCODE_MINUS);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_EQUALS, KeyEvent.KEYCODE_EQUALS);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Q, KeyEvent.KEYCODE_Q);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_W, KeyEvent.KEYCODE_W);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_E, KeyEvent.KEYCODE_E);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_R, KeyEvent.KEYCODE_R);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_T, KeyEvent.KEYCODE_T);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Y, KeyEvent.KEYCODE_Y);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_U, KeyEvent.KEYCODE_U);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_I, KeyEvent.KEYCODE_I);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_O, KeyEvent.KEYCODE_O);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_P, KeyEvent.KEYCODE_P);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_LEFT_BRACKET, KeyEvent.KEYCODE_LEFT_BRACKET);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_RIGHT_BRACKET, KeyEvent.KEYCODE_RIGHT_BRACKET);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_A, KeyEvent.KEYCODE_A);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_S, KeyEvent.KEYCODE_S);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_D, KeyEvent.KEYCODE_D);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_F, KeyEvent.KEYCODE_F);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_G, KeyEvent.KEYCODE_G);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_H, KeyEvent.KEYCODE_H);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_J, KeyEvent.KEYCODE_J);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_K, KeyEvent.KEYCODE_K);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_L, KeyEvent.KEYCODE_L);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_SEMICOLON, KeyEvent.KEYCODE_SEMICOLON);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_APOSTROPHE, KeyEvent.KEYCODE_APOSTROPHE);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_GRAVE, KeyEvent.KEYCODE_GRAVE);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_BACKSLASH1, KeyEvent.KEYCODE_BACKSLASH);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_Z, KeyEvent.KEYCODE_Z);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_X, KeyEvent.KEYCODE_X);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_C, KeyEvent.KEYCODE_C);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_V, KeyEvent.KEYCODE_V);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_B, KeyEvent.KEYCODE_B);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_N, KeyEvent.KEYCODE_N);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_M, KeyEvent.KEYCODE_M);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_COMMA, KeyEvent.KEYCODE_COMMA);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_PERIOD, KeyEvent.KEYCODE_PERIOD);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_SLASH, KeyEvent.KEYCODE_SLASH);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_BACKSLASH2, KeyEvent.KEYCODE_BACKSLASH);
+        DEFAULT_KEYCODE_FOR_SCANCODE.put(SCANCODE_YEN, KeyEvent.KEYCODE_YEN);
+    }
+
+    private LayoutKey[][] mKeys = null;
+    private EnterKey mEnterKey = null;
+
+    public PhysicalKeyLayout(@NonNull KeyCharacterMap kcm, @Nullable KeyboardLayout layout) {
+        initLayoutKeys(kcm, layout);
+    }
+
+    private void initLayoutKeys(KeyCharacterMap kcm, KeyboardLayout layout) {
+        if (layout == null) {
+            createIsoLayout(kcm);
+            return;
+        }
+        if (layout.isAnsiLayout()) {
+            createAnsiLayout(kcm);
+        } else if (layout.isJisLayout()) {
+            createJisLayout(kcm);
+        } else {
+            createIsoLayout(kcm);
+        }
+    }
+
+    public LayoutKey[][] getKeys() {
+        return mKeys;
+    }
+
+    /**
+     * @return Special enter key (if required) that can span multiple rows like ISO enter key.
+     */
+    @Nullable
+    public EnterKey getEnterKey() {
+        return mEnterKey;
+    }
+
+    private void createAnsiLayout(KeyCharacterMap kcm) {
+        mKeys = new LayoutKey[][]{
+                {
+                        getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+                        getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+                        getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+                        getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+                        getKey(kcm, SCANCODE_MINUS), getKey(kcm, SCANCODE_EQUALS),
+                        getKey(KeyEvent.KEYCODE_DEL, 1.5F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_Q),
+                        getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+                        getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+                        getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+                        getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+                        getKey(kcm, SCANCODE_BACKSLASH1)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_CAPS_LOCK, 1.75F),
+                        getKey(kcm, SCANCODE_A), getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D),
+                        getKey(kcm, SCANCODE_F), getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H),
+                        getKey(kcm, SCANCODE_J), getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+                        getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+                        getKey(KeyEvent.KEYCODE_ENTER, 1.75F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 2.5F),
+                        getKey(kcm, SCANCODE_Z), getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C),
+                        getKey(kcm, SCANCODE_V), getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N),
+                        getKey(kcm, SCANCODE_M), getKey(kcm, SCANCODE_COMMA),
+                        getKey(kcm, SCANCODE_PERIOD), getKey(kcm, SCANCODE_SLASH),
+                        getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.5F),
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+                        getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_SPACE, 6.5F),
+                        getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+                        getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+                }
+        };
+    }
+
+    private void createIsoLayout(KeyCharacterMap kcm) {
+        mKeys = new LayoutKey[][]{
+                {
+                        getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+                        getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+                        getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+                        getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+                        getKey(kcm, SCANCODE_MINUS), getKey(kcm, SCANCODE_EQUALS),
+                        getKey(KeyEvent.KEYCODE_DEL, 1.5F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_TAB, 1.15F), getKey(kcm, SCANCODE_Q),
+                        getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+                        getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+                        getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+                        getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+                        getKey(KeyEvent.KEYCODE_ENTER, 1.35F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_A),
+                        getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D), getKey(kcm, SCANCODE_F),
+                        getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H), getKey(kcm, SCANCODE_J),
+                        getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+                        getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+                        getKey(kcm, SCANCODE_BACKSLASH1),
+                        getKey(KeyEvent.KEYCODE_ENTER, 1.0F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 1.15F),
+                        getKey(kcm, SCANCODE_BACKSLASH2), getKey(kcm, SCANCODE_Z),
+                        getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C), getKey(kcm, SCANCODE_V),
+                        getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N), getKey(kcm, SCANCODE_M),
+                        getKey(kcm, SCANCODE_COMMA), getKey(kcm, SCANCODE_PERIOD),
+                        getKey(kcm, SCANCODE_SLASH),
+                        getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.35F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+                        getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_SPACE, 6.5F),
+                        getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+                        getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+                }
+        };
+        mEnterKey = new EnterKey(1, 13, 1.35F, 1.0F);
+    }
+
+    private void createJisLayout(KeyCharacterMap kcm) {
+        mKeys = new LayoutKey[][]{
+                {
+                        getKey(kcm, SCANCODE_GRAVE), getKey(kcm, SCANCODE_1),
+                        getKey(kcm, SCANCODE_2), getKey(kcm, SCANCODE_3), getKey(kcm, SCANCODE_4),
+                        getKey(kcm, SCANCODE_5), getKey(kcm, SCANCODE_6), getKey(kcm, SCANCODE_7),
+                        getKey(kcm, SCANCODE_8), getKey(kcm, SCANCODE_9), getKey(kcm, SCANCODE_0),
+                        getKey(kcm, SCANCODE_MINUS, 0.8F), getKey(kcm, SCANCODE_EQUALS, 0.8f),
+                        getKey(kcm, SCANCODE_YEN, 0.8f), getKey(KeyEvent.KEYCODE_DEL, 1.1F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_TAB, 1.15F), getKey(kcm, SCANCODE_Q),
+                        getKey(kcm, SCANCODE_W), getKey(kcm, SCANCODE_E), getKey(kcm, SCANCODE_R),
+                        getKey(kcm, SCANCODE_T), getKey(kcm, SCANCODE_Y), getKey(kcm, SCANCODE_U),
+                        getKey(kcm, SCANCODE_I), getKey(kcm, SCANCODE_O), getKey(kcm, SCANCODE_P),
+                        getKey(kcm, SCANCODE_LEFT_BRACKET), getKey(kcm, SCANCODE_RIGHT_BRACKET),
+                        getKey(KeyEvent.KEYCODE_ENTER, 1.35F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_TAB, 1.5F), getKey(kcm, SCANCODE_A),
+                        getKey(kcm, SCANCODE_S), getKey(kcm, SCANCODE_D), getKey(kcm, SCANCODE_F),
+                        getKey(kcm, SCANCODE_G), getKey(kcm, SCANCODE_H), getKey(kcm, SCANCODE_J),
+                        getKey(kcm, SCANCODE_K), getKey(kcm, SCANCODE_L),
+                        getKey(kcm, SCANCODE_SEMICOLON), getKey(kcm, SCANCODE_APOSTROPHE),
+                        getKey(kcm, SCANCODE_BACKSLASH2),
+                        getKey(KeyEvent.KEYCODE_ENTER, 1.0F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_SHIFT_LEFT, 1.15F),
+                        getKey(kcm, SCANCODE_Z), getKey(kcm, SCANCODE_X), getKey(kcm, SCANCODE_C),
+                        getKey(kcm, SCANCODE_V), getKey(kcm, SCANCODE_B), getKey(kcm, SCANCODE_N),
+                        getKey(kcm, SCANCODE_M), getKey(kcm, SCANCODE_COMMA),
+                        getKey(kcm, SCANCODE_PERIOD), getKey(kcm, SCANCODE_SLASH),
+                        getKey(kcm, SCANCODE_BACKSLASH1),
+                        getKey(KeyEvent.KEYCODE_SHIFT_RIGHT, 2.35F)
+                },
+                {
+                        getKey(KeyEvent.KEYCODE_CTRL_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_FUNCTION, 1.0F),
+                        getKey(KeyEvent.KEYCODE_META_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_ALT_LEFT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+                        getKey(KeyEvent.KEYCODE_SPACE, 3.5F),
+                        getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+                        getKey(KeyEvent.KEYCODE_UNKNOWN, 1.0F),
+                        getKey(KeyEvent.KEYCODE_ALT_RIGHT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_META_RIGHT, 1.0F),
+                        getKey(KeyEvent.KEYCODE_MENU, 1.0F),
+                        getKey(KeyEvent.KEYCODE_CTRL_RIGHT, 1.0F),
+                }
+        };
+        mEnterKey = new EnterKey(1, 13, 1.35F, 1.0F);
+    }
+
+    private static LayoutKey getKey(KeyCharacterMap kcm, int scanCode, float keyWeight) {
+        int keyCode = kcm.getMappedKeyOrDefault(scanCode,
+                DEFAULT_KEYCODE_FOR_SCANCODE.get(scanCode, KeyEvent.KEYCODE_UNKNOWN));
+        return new LayoutKey(keyCode, scanCode, keyWeight, new KeyGlyph(kcm, keyCode));
+    }
+
+    private static LayoutKey getKey(KeyCharacterMap kcm, int scanCode) {
+        return getKey(kcm, scanCode, 1.0F);
+    }
+
+    private static String getKeyText(KeyCharacterMap kcm, int keyCode, int modifierState) {
+        if (isSpecialKey(keyCode)) {
+            return "";
+        }
+        int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+        if (Character.isValidCodePoint(utf8Char)) {
+            return String.valueOf(Character.toChars(utf8Char)).toUpperCase(Locale.getDefault());
+        } else {
+            return String.valueOf(kcm.getDisplayLabel(keyCode)).toUpperCase(Locale.getDefault());
+        }
+    }
+
+    private static LayoutKey getKey(int keyCode, float keyWeight) {
+        return new LayoutKey(keyCode, keyCode, keyWeight, null);
+    }
+
+    /**
+     * Util function that tells if a key corresponds to a special key which are keys on a Physical
+     * layout that perform some special action like modifier keys, enter key, space key, character
+     * set changing keys, etc.
+     */
+    private static boolean isSpecialKey(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DEL:
+            case KeyEvent.KEYCODE_TAB:
+            case KeyEvent.KEYCODE_CAPS_LOCK:
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_SHIFT_LEFT:
+            case KeyEvent.KEYCODE_SHIFT_RIGHT:
+            case KeyEvent.KEYCODE_CTRL_LEFT:
+            case KeyEvent.KEYCODE_CTRL_RIGHT:
+            case KeyEvent.KEYCODE_FUNCTION:
+            case KeyEvent.KEYCODE_ALT_LEFT:
+            case KeyEvent.KEYCODE_ALT_RIGHT:
+            case KeyEvent.KEYCODE_META_LEFT:
+            case KeyEvent.KEYCODE_META_RIGHT:
+            case KeyEvent.KEYCODE_SPACE:
+            case KeyEvent.KEYCODE_MENU:
+            case KeyEvent.KEYCODE_UNKNOWN:
+                return true;
+        }
+        return false;
+    }
+
+    public static boolean isSpecialKey(LayoutKey key) {
+        return isSpecialKey(key.keyCode);
+    }
+
+    public static boolean isKeyPositionUnsure(LayoutKey key) {
+        switch (key.scanCode) {
+            case SCANCODE_GRAVE:
+            case SCANCODE_BACKSLASH1:
+            case SCANCODE_BACKSLASH2:
+                return true;
+        }
+        return false;
+    }
+
+    public record LayoutKey(int keyCode, int scanCode, float keyWeight, KeyGlyph glyph) {}
+    public record EnterKey(int row, int column, float topKeyWeight, float bottomKeyWeight) {}
+
+    public static class KeyGlyph {
+        private final String mBaseText;
+        private final String mShiftText;
+        private final String mAltGrText;
+
+        public KeyGlyph(KeyCharacterMap kcm, int keyCode) {
+            mBaseText = getKeyText(kcm, keyCode, 0);
+            mShiftText = getKeyText(kcm, keyCode,
+                    KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON);
+            mAltGrText = getKeyText(kcm, keyCode,
+                    KeyEvent.META_ALT_ON | KeyEvent.META_ALT_RIGHT_ON);
+        }
+
+        public String getBaseText() {
+            return mBaseText;
+        }
+
+        public String getShiftText() {
+            return mShiftText;
+        }
+
+        public String getAltGrText() {
+            return mAltGrText;
+        }
+
+        public boolean hasBaseText() {
+            return !TextUtils.isEmpty(mBaseText);
+        }
+
+        public boolean hasValidShiftText() {
+            return !TextUtils.isEmpty(mShiftText) && !TextUtils.equals(mBaseText, mShiftText);
+        }
+
+        public boolean hasValidAltGrText() {
+            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 24d1c95..1b8d925 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -104,7 +104,11 @@
      */
     void hide(boolean remove) {
         if (getDecorView() != null) {
-            getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
+            if (remove) {
+                mWindowManager.removeViewImmediate(getDecorView());
+            } else {
+                getDecorView().setVisibility(View.INVISIBLE);
+            }
         }
     }
 
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 7f6ec58..cf47786 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -16,14 +16,12 @@
 
 package android.inputmethodservice;
 
-import static android.inputmethodservice.SoftInputWindowProto.BOUNDS;
 import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
 import android.app.Dialog;
-import android.graphics.Rect;
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Log;
@@ -45,7 +43,6 @@
     private static final String TAG = "SoftInputWindow";
 
     private final KeyEvent.DispatcherState mDispatcherState;
-    private final Rect mBounds = new Rect();
     private final InputMethodService mService;
 
     @Retention(SOURCE)
@@ -150,22 +147,6 @@
     }
 
     @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        getWindow().getDecorView().getHitRect(mBounds);
-
-        if (ev.isWithinBoundsNoHistory(mBounds.left, mBounds.top,
-                mBounds.right - 1, mBounds.bottom - 1)) {
-            return super.dispatchTouchEvent(ev);
-        } else {
-            MotionEvent temp = ev.clampNoHistory(mBounds.left, mBounds.top,
-                    mBounds.right - 1, mBounds.bottom - 1);
-            boolean handled = super.dispatchTouchEvent(temp);
-            temp.recycle();
-            return handled;
-        }
-    }
-
-    @Override
     public void show() {
         switch (mWindowState) {
             case WindowState.TOKEN_PENDING:
@@ -273,7 +254,6 @@
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        mBounds.dumpDebug(proto, BOUNDS);
         proto.write(WINDOW_STATE, mWindowState);
         proto.end(token);
     }
diff --git a/core/java/android/nfc/NfcAntennaInfo.java b/core/java/android/nfc/NfcAntennaInfo.java
index d54fcd2..b002ca2 100644
--- a/core/java/android/nfc/NfcAntennaInfo.java
+++ b/core/java/android/nfc/NfcAntennaInfo.java
@@ -85,8 +85,8 @@
         this.mDeviceHeight = in.readInt();
         this.mDeviceFoldable = in.readByte() != 0;
         this.mAvailableNfcAntennas = new ArrayList<>();
-        in.readParcelableList(this.mAvailableNfcAntennas,
-                AvailableNfcAntenna.class.getClassLoader());
+        in.readTypedList(this.mAvailableNfcAntennas,
+                AvailableNfcAntenna.CREATOR);
     }
 
     public static final @NonNull Parcelable.Creator<NfcAntennaInfo> CREATOR =
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f40efc8..218d4bb 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -186,6 +186,8 @@
     /**
      * Get the binder transaction observer for this process.
      *
+     * TODO(b/299356196): only applies to Java code, not C++/Rust
+     *
      * @hide
      */
     public static void setObserver(@Nullable BinderInternal.Observer observer) {
@@ -202,6 +204,8 @@
      * that require a result must be sent as {@link IBinder#FLAG_ONEWAY} calls
      * which deliver results through a callback interface.
      *
+     * TODO(b/299355525): only applies to Java code, not C++/Rust
+     *
      * @hide
      */
     public static void setWarnOnBlocking(boolean warnOnBlocking) {
@@ -218,6 +222,8 @@
      * interfaces hosted by package that could be upgraded or replaced,
      * otherwise you risk system instability if that remote interface wedges.
      *
+     * TODO(b/299355525): only applies to Java code, not C++/Rust
+     *
      * @hide
      */
     public static IBinder allowBlocking(IBinder binder) {
@@ -1307,6 +1313,8 @@
             int callingUid) {
         // Make sure the observer won't change while processing a transaction.
         final BinderInternal.Observer observer = sObserver;
+
+        // TODO(b/299356196): observer should also observe transactions in native code
         final CallSession callSession =
                 observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
         // Theoretically, we should call transact, which will call onTransact,
@@ -1329,7 +1337,7 @@
 
         final boolean tracingEnabled = tagEnabled && transactionTraceName != null;
         try {
-            // TODO - this logic should not be in Java - it should be in native
+            // TODO(b/299356201) - this logic should not be in Java - it should be in native
             // code in libbinder so that it works for all binder users.
             final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
             if (heavyHitterWatcher != null && callingUid != -1) {
@@ -1340,9 +1348,9 @@
                 Trace.traceBegin(Trace.TRACE_TAG_AIDL, transactionTraceName);
             }
 
-            // TODO - this logic should not be in Java - it should be in native
-            // code in libbinder so that it works for all binder users. Further,
-            // this should not re-use flags.
+            // TODO(b/299353919) - this logic should not be in Java - it should be
+            // in native code in libbinder so that it works for all binder users.
+            // Further, this should not re-use flags.
             if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
                 AppOpsManager.startNotedAppOpsCollection(callingUid);
                 try {
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1929a4d..ada5532 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -227,7 +227,7 @@
                 Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
                         + " total = " + totalSize);
                 mWarnBucketSize += WARN_INCREMENT;
-                if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+                if (totalSize >= CRASH_AT_SIZE) {
                     // Use the number of uncleared entries to determine whether we should
                     // really report a histogram and crash. We don't want to fundamentally
                     // change behavior for a debuggable process, so we GC only if we are
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 62d9c69..c527cb5 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppGlobals;
@@ -1953,6 +1954,23 @@
      */
     public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
 
+    /**
+     * Retrieves the RSS memory used by the process as given by the status file.
+     */
+    @FlaggedApi(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+    public static native long getRss();
+
+    /**
+     * Retrieves the RSS memory used by the process as given by the status file. Optionally supply a
+     * long array of up to 4 entries to retrieve the total memtrack reported size, memtrack
+     * graphics, memtrack gl, and memtrack other.
+     *
+     * @return The RSS memory usage, or 0 if retrieval failed (i.e. the PID is gone).
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+    public static native long getRss(int pid, long[] outMemtrack);
+
     /** @hide */
     public static final int MEMINFO_TOTAL = 0;
     /** @hide */
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index b210c46..e96c24d 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -245,7 +245,7 @@
     public static boolean isDeclared(@NonNull String name) {
         try {
             return getIServiceManager().isDeclared(name);
-        } catch (RemoteException e) {
+        } catch (RemoteException | SecurityException e) {
             Log.e(TAG, "error in isDeclared", e);
             return false;
         }
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
new file mode 100644
index 0000000..77be5d4
--- /dev/null
+++ b/core/java/android/permission/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.permission.flags"
+
+flag {
+  name: "device_aware_permission_apis"
+  namespace: "permissions"
+  description: "enable device aware permission APIs"
+  bug: "274852670"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7e71134f..c19c20c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3596,12 +3596,9 @@
                 }
 
                 Bundle b;
-                // b/252663068: if we're in system server and the caller did not call
+                // If we're in system server and the caller did not call
                 // clearCallingIdentity, the read would fail due to mismatched AttributionSources.
-                // TODO(b/256013480): remove this bypass after fixing the callers in system server.
-                if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
-                        && Settings.isInSystemServer()
-                        && Binder.getCallingUid() != Process.myUid()) {
+                if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
                     final long token = Binder.clearCallingIdentity();
                     try {
                         // Fetch all flags for the namespace at once for caching purposes
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index cf3707b..a391571 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4915,6 +4915,14 @@
         public static final String COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER =
                 "satellite_attach_enabled_for_carrier";
 
+        /**
+         * TelephonyProvider column name to identify eSIM profile of a non-terrestrial network.
+         * By default, it's disabled.
+         *
+         * @hide
+         */
+        public static final String COLUMN_IS_NTN = "is_ntn";
+
         /** All columns in {@link SimInfo} table. */
         private static final List<String> ALL_COLUMNS = List.of(
                 COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
@@ -4985,7 +4993,8 @@
                 COLUMN_TP_MESSAGE_REF,
                 COLUMN_USER_HANDLE,
                 COLUMN_SATELLITE_ENABLED,
-                COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
+                COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                COLUMN_IS_NTN
         );
 
         /**
diff --git a/core/java/android/security/TEST_MAPPING b/core/java/android/security/TEST_MAPPING
index 7e43381..5a679b1 100644
--- a/core/java/android/security/TEST_MAPPING
+++ b/core/java/android/security/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-    "postsubmit": [
+    "presubmit": [
         {
             "name": "CtsSecurityTestCases",
             "options": [
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 00c30b1..83f9662 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -63,6 +63,10 @@
     private static final String TAG_AUTOFILL_SERVICE = "autofill-service";
     private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package";
 
+    private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+            new ComponentName("com.android.credentialmanager",
+                    "com.android.credentialmanager.autofill.CredentialAutofillService");
+
     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
         try {
@@ -307,6 +311,11 @@
         for (ResolveInfo resolveInfo : resolveInfos) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
             try {
+                if (serviceInfo != null && isCredentialManagerAutofillService(
+                        serviceInfo.getComponentName())) {
+                    // Skip this service as it is for internal use only
+                    continue;
+                }
                 services.add(new AutofillServiceInfo(context, serviceInfo));
             } catch (SecurityException e) {
                 // Service does not declare the proper permission, ignore it.
@@ -316,6 +325,13 @@
         return services;
     }
 
+    private static boolean isCredentialManagerAutofillService(ComponentName componentName) {
+        if (componentName == null) {
+            return false;
+        }
+        return componentName.equals(CREDMAN_SERVICE_COMPONENT_NAME);
+    }
+
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index b48b7ec..3f41c56 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -46,6 +46,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SharedMemory;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Log;
@@ -131,6 +132,9 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     static final long MULTIPLE_ACTIVE_HOTWORD_DETECTORS = 193232191L;
 
+    private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
+            SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
+
     IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
         @Override
         public void ready() {
@@ -947,6 +951,10 @@
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
+        if (!SYSPROP_VISUAL_QUERY_SERVICE_ENABLED) {
+            throw new IllegalStateException("VisualQueryDetectionService is not enabled on this "
+                    + "system. Please set ro.hotword.visual_query_service_enabled to true.");
+        }
         if (mSystemService == null) {
             throw new IllegalStateException("Not available until onReady() is called");
         }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 0ed275c..2906d86 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -187,14 +187,6 @@
     public static final String SETTINGS_FLASH_NOTIFICATIONS = "settings_flash_notifications";
 
     /**
-     * Flag to disable/enable showing udfps enroll view in settings. If it's disabled, udfps enroll
-     * view is shown in system ui.
-     * @hide
-     */
-    public static final String SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS =
-            "settings_show_udfps_enroll_in_settings";
-
-    /**
      * Flag to enable lock screen credentials transfer API in Android U.
      * @hide
      */
@@ -208,14 +200,6 @@
     public static final String SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION =
             "settings_remote_device_credential_validation";
 
-    // TODO(b/295516544): Remove this when trunk stable feature flag is available.
-    /**
-     * Flag to enable / disable the Private Space Settings. It's disabled by default.
-     * @hide
-     */
-    public static final String SETTINGS_PRIVATE_SPACE_SETTINGS =
-            "settings_private_space_settings";
-
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -258,13 +242,11 @@
         DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false");
         DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
         DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
-        DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true");
         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");
         // TODO: b/298454866 Replace with Trunk Stable Feature Flag
         DEFAULT_FLAGS.put(SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS, "false");
-        DEFAULT_FLAGS.put(SETTINGS_PRIVATE_SPACE_SETTINGS, "false");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index ece069f..c4660c4 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -111,7 +111,7 @@
     /**
      *  Regular expression to match all IANA top-level domains.
      *
-     *  List accurate as of 2015/11/24.  List taken from:
+     *  List accurate as of 2023/09/11.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
      *  This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
      *
@@ -119,121 +119,167 @@
      */
     static final String IANA_TOP_LEVEL_DOMAINS =
         "(?:"
-        + "(?:aaa|aarp|abb|abbott|abogado|academy|accenture|accountant|accountants|aco|active"
-        + "|actor|ads|adult|aeg|aero|afl|agency|aig|airforce|airtel|allfinanz|alsace|amica|amsterdam"
-        + "|android|apartments|app|apple|aquarelle|aramco|archi|army|arpa|arte|asia|associates"
-        + "|attorney|auction|audio|auto|autos|axa|azure|a[cdefgilmoqrstuwxz])"
-        + "|(?:band|bank|bar|barcelona|barclaycard|barclays|bargains|bauhaus|bayern|bbc|bbva"
-        + "|bcn|beats|beer|bentley|berlin|best|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
-        + "|blackfriday|bloomberg|blue|bms|bmw|bnl|bnpparibas|boats|bom|bond|boo|boots|boutique"
-        + "|bradesco|bridgestone|broadway|broker|brother|brussels|budapest|build|builders|business"
-        + "|buzz|bzh|b[abdefghijmnorstvwyz])"
-        + "|(?:cab|cafe|cal|camera|camp|cancerresearch|canon|capetown|capital|car|caravan|cards"
-        + "|care|career|careers|cars|cartier|casa|cash|casino|cat|catering|cba|cbn|ceb|center|ceo"
-        + "|cern|cfa|cfd|chanel|channel|chat|cheap|chloe|christmas|chrome|church|cipriani|cisco"
-        + "|citic|city|cityeats|claims|cleaning|click|clinic|clothing|cloud|club|clubmed|coach"
-        + "|codes|coffee|college|cologne|com|commbank|community|company|computer|comsec|condos"
-        + "|construction|consulting|contractors|cooking|cool|coop|corsica|country|coupons|courses"
-        + "|credit|creditcard|creditunion|cricket|crown|crs|cruises|csc|cuisinella|cymru|cyou|c[acdfghiklmnoruvwxyz])"
-        + "|(?:dabur|dad|dance|date|dating|datsun|day|dclk|deals|degree|delivery|dell|delta"
-        + "|democrat|dental|dentist|desi|design|dev|diamonds|diet|digital|direct|directory|discount"
-        + "|dnp|docs|dog|doha|domains|doosan|download|drive|durban|dvag|d[ejkmoz])"
-        + "|(?:earth|eat|edu|education|email|emerck|energy|engineer|engineering|enterprises"
-        + "|epson|equipment|erni|esq|estate|eurovision|eus|events|everbank|exchange|expert|exposed"
-        + "|express|e[cegrstu])"
-        + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|fashion|feedback|ferrero|film"
-        + "|final|finance|financial|firmdale|fish|fishing|fit|fitness|flights|florist|flowers|flsmidth"
-        + "|fly|foo|football|forex|forsale|forum|foundation|frl|frogans|fund|furniture|futbol|fyi"
-        + "|f[ijkmor])"
-        + "|(?:gal|gallery|game|garden|gbiz|gdn|gea|gent|genting|ggee|gift|gifts|gives|giving"
-        + "|glass|gle|global|globo|gmail|gmo|gmx|gold|goldpoint|golf|goo|goog|google|gop|gov|grainger"
-        + "|graphics|gratis|green|gripe|group|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
-        + "|(?:hamburg|hangout|haus|healthcare|help|here|hermes|hiphop|hitachi|hiv|hockey|holdings"
-        + "|holiday|homedepot|homes|honda|horse|host|hosting|hoteles|hotmail|house|how|hsbc|hyundai"
-        + "|h[kmnrtu])"
-        + "|(?:ibm|icbc|ice|icu|ifm|iinet|immo|immobilien|industries|infiniti|info|ing|ink|institute"
-        + "|insure|int|international|investments|ipiranga|irish|ist|istanbul|itau|iwc|i[delmnoqrst])"
-        + "|(?:jaguar|java|jcb|jetzt|jewelry|jlc|jll|jobs|joburg|jprs|juegos|j[emop])"
-        + "|(?:kaufen|kddi|kia|kim|kinder|kitchen|kiwi|koeln|komatsu|krd|kred|kyoto|k[eghimnprwyz])"
-        + "|(?:lacaixa|lancaster|land|landrover|lasalle|lat|latrobe|law|lawyer|lds|lease|leclerc"
-        + "|legal|lexus|lgbt|liaison|lidl|life|lifestyle|lighting|limited|limo|linde|link|live"
-        + "|lixil|loan|loans|lol|london|lotte|lotto|love|ltd|ltda|lupin|luxe|luxury|l[abcikrstuvy])"
-        + "|(?:madrid|maif|maison|man|management|mango|market|marketing|markets|marriott|mba"
-        + "|media|meet|melbourne|meme|memorial|men|menu|meo|miami|microsoft|mil|mini|mma|mobi|moda"
-        + "|moe|moi|mom|monash|money|montblanc|mormon|mortgage|moscow|motorcycles|mov|movie|movistar"
-        + "|mtn|mtpc|mtr|museum|mutuelle|m[acdeghklmnopqrstuvwxyz])"
-        + "|(?:nadex|nagoya|name|navy|nec|net|netbank|network|neustar|new|news|nexus|ngo|nhk"
-        + "|nico|ninja|nissan|nokia|nra|nrw|ntt|nyc|n[acefgilopruz])"
-        + "|(?:obi|office|okinawa|omega|one|ong|onl|online|ooo|oracle|orange|org|organic|osaka"
-        + "|otsuka|ovh|om)"
-        + "|(?:page|panerai|paris|partners|parts|party|pet|pharmacy|philips|photo|photography"
-        + "|photos|physio|piaget|pics|pictet|pictures|ping|pink|pizza|place|play|playstation|plumbing"
-        + "|plus|pohl|poker|porn|post|praxi|press|pro|prod|productions|prof|properties|property"
-        + "|protection|pub|p[aefghklmnrstwy])"
-        + "|(?:qpon|quebec|qa)"
-        + "|(?:racing|realtor|realty|recipes|red|redstone|rehab|reise|reisen|reit|ren|rent|rentals"
-        + "|repair|report|republican|rest|restaurant|review|reviews|rich|ricoh|rio|rip|rocher|rocks"
-        + "|rodeo|rsvp|ruhr|run|rwe|ryukyu|r[eosuw])"
-        + "|(?:saarland|sakura|sale|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|saxo"
-        + "|sbs|sca|scb|schmidt|scholarships|school|schule|schwarz|science|scor|scot|seat|security"
-        + "|seek|sener|services|seven|sew|sex|sexy|shiksha|shoes|show|shriram|singles|site|ski"
-        + "|sky|skype|sncf|soccer|social|software|sohu|solar|solutions|sony|soy|space|spiegel|spreadbetting"
-        + "|srl|stada|starhub|statoil|stc|stcgroup|stockholm|studio|study|style|sucks|supplies"
-        + "|supply|support|surf|surgery|suzuki|swatch|swiss|sydney|systems|s[abcdeghijklmnortuvxyz])"
-        + "|(?:tab|taipei|tatamotors|tatar|tattoo|tax|taxi|team|tech|technology|tel|telefonica"
-        + "|temasek|tennis|thd|theater|theatre|tickets|tienda|tips|tires|tirol|today|tokyo|tools"
-        + "|top|toray|toshiba|tours|town|toyota|toys|trade|trading|training|travel|trust|tui|t[cdfghjklmnortvwz])"
-        + "|(?:ubs|university|uno|uol|u[agksyz])"
-        + "|(?:vacations|vana|vegas|ventures|versicherung|vet|viajes|video|villas|vin|virgin"
-        + "|vision|vista|vistaprint|viva|vlaanderen|vodka|vote|voting|voto|voyage|v[aceginu])"
-        + "|(?:wales|walter|wang|watch|webcam|website|wed|wedding|weir|whoswho|wien|wiki|williamhill"
-        + "|win|windows|wine|wme|work|works|world|wtc|wtf|w[fs])"
-        + "|(?:\u03b5\u03bb|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438|\u043a\u043e\u043c|\u043c\u043a\u0434"
+        + "(?:aaa|aarp|abb|abbott|abbvie|abc|able|abogado|abudhabi|academy|accenture|accountant"
+        + "|accountants|aco|actor|ads|adult|aeg|aero|aetna|afl|africa|agakhan|agency|aig|airbus"
+        + "|airforce|airtel|akdn|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|amazon|americanexpress"
+        + "|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|aol|apartments"
+        + "|app|apple|aquarelle|arab|aramco|archi|army|arpa|art|arte|asda|asia|associates|athleta"
+        + "|attorney|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aws|axa|azure"
+        + "|a[cdefgilmoqrstuwxz])"
+        + "|(?:baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays"
+        + "|barefoot|bargains|baseball|basketball|bauhaus|bayern|bbc|bbt|bbva|bcg|bcn|beats|beauty"
+        + "|beer|bentley|berlin|best|bestbuy|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
+        + "|blackfriday|blockbuster|blog|bloomberg|blue|bms|bmw|bnpparibas|boats|boehringer|bofa"
+        + "|bom|bond|boo|book|booking|bosch|bostik|boston|bot|boutique|box|bradesco|bridgestone"
+        + "|broadway|broker|brother|brussels|build|builders|business|buy|buzz|bzh|b[abdefghijmnorstvwyz])"
+        + "|(?:cab|cafe|cal|call|calvinklein|cam|camera|camp|canon|capetown|capital|capitalone"
+        + "|car|caravan|cards|care|career|careers|cars|casa|case|cash|casino|cat|catering|catholic"
+        + "|cba|cbn|cbre|cbs|center|ceo|cern|cfa|cfd|chanel|channel|charity|chase|chat|cheap|chintai"
+        + "|christmas|chrome|church|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|claims"
+        + "|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|coach|codes|coffee|college"
+        + "|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction"
+        + "|consulting|contact|contractors|cooking|cool|coop|corsica|country|coupon|coupons|courses"
+        + "|cpa|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|cuisinella|cymru"
+        + "|cyou|c[acdfghiklmnoruvwxyz])"
+        + "|(?:dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|deal|dealer|deals|degree"
+        + "|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet"
+        + "|digital|direct|directory|discount|discover|dish|diy|dnp|docs|doctor|dog|domains|dot"
+        + "|download|drive|dtv|dubai|dunlop|dupont|durban|dvag|dvr|d[ejkmoz])"
+        + "|(?:earth|eat|eco|edeka|edu|education|email|emerck|energy|engineer|engineering|enterprises"
+        + "|epson|equipment|ericsson|erni|esq|estate|etisalat|eurovision|eus|events|exchange|expert"
+        + "|exposed|express|extraspace|e[cegrstu])"
+        + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback"
+        + "|ferrari|ferrero|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale"
+        + "|fish|fishing|fit|fitness|flickr|flights|flir|florist|flowers|fly|foo|food|football"
+        + "|ford|forex|forsale|forum|foundation|fox|free|fresenius|frl|frogans|frontdoor|frontier"
+        + "|ftr|fujitsu|fun|fund|furniture|futbol|fyi|f[ijkmor])"
+        + "|(?:gal|gallery|gallo|gallup|game|games|gap|garden|gay|gbiz|gdn|gea|gent|genting"
+        + "|george|ggee|gift|gifts|gives|giving|glass|gle|global|globo|gmail|gmbh|gmo|gmx|godaddy"
+        + "|gold|goldpoint|golf|goo|goodyear|goog|google|gop|got|gov|grainger|graphics|gratis|green"
+        + "|gripe|grocery|group|guardian|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
+        + "|(?:hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here"
+        + "|hermes|hiphop|hisamitsu|hitachi|hiv|hkt|hockey|holdings|holiday|homedepot|homegoods"
+        + "|homes|homesense|honda|horse|hospital|host|hosting|hot|hotels|hotmail|house|how|hsbc"
+        + "|hughes|hyatt|hyundai|h[kmnrtu])"
+        + "|(?:ibm|icbc|ice|icu|ieee|ifm|ikano|imamat|imdb|immo|immobilien|inc|industries|infiniti"
+        + "|info|ing|ink|institute|insurance|insure|int|international|intuit|investments|ipiranga"
+        + "|irish|ismaili|ist|istanbul|itau|itv|i[delmnoqrst])"
+        + "|(?:jaguar|java|jcb|jeep|jetzt|jewelry|jio|jll|jmp|jnj|jobs|joburg|jot|joy|jpmorgan"
+        + "|jprs|juegos|juniper|j[emop])"
+        + "|(?:kaufen|kddi|kerryhotels|kerrylogistics|kerryproperties|kfh|kia|kids|kim|kinder"
+        + "|kindle|kitchen|kiwi|koeln|komatsu|kosher|kpmg|kpn|krd|kred|kuokgroup|kyoto|k[eghimnprwyz])"
+        + "|(?:lacaixa|lamborghini|lamer|lancaster|land|landrover|lanxess|lasalle|lat|latino"
+        + "|latrobe|law|lawyer|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|lidl|life|lifeinsurance"
+        + "|lifestyle|lighting|like|lilly|limited|limo|lincoln|link|lipsy|live|living|llc|llp|loan"
+        + "|loans|locker|locus|lol|london|lotte|lotto|love|lpl|lplfinancial|ltd|ltda|lundbeck|luxe"
+        + "|luxury|l[abcikrstuvy])"
+        + "|(?:madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott"
+        + "|marshalls|mattel|mba|mckinsey|med|media|meet|melbourne|meme|memorial|men|menu|merckmsd"
+        + "|miami|microsoft|mil|mini|mint|mit|mitsubishi|mlb|mls|mma|mobi|mobile|moda|moe|moi|mom"
+        + "|monash|money|monster|mormon|mortgage|moscow|moto|motorcycles|mov|movie|msd|mtn|mtr"
+        + "|museum|music|m[acdeghklmnopqrstuvwxyz])"
+        + "|(?:nab|nagoya|name|natura|navy|nba|nec|net|netbank|netflix|network|neustar|new|news"
+        + "|next|nextdirect|nexus|nfl|ngo|nhk|nico|nike|nikon|ninja|nissan|nissay|nokia|norton"
+        + "|now|nowruz|nowtv|nra|nrw|ntt|nyc|n[acefgilopruz])"
+        + "|(?:obi|observer|office|okinawa|olayan|olayangroup|oldnavy|ollo|omega|one|ong|onl"
+        + "|online|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|om)"
+        + "|(?:page|panasonic|paris|pars|partners|parts|party|pay|pccw|pet|pfizer|pharmacy|phd"
+        + "|philips|phone|photo|photography|photos|physio|pics|pictet|pictures|pid|pin|ping|pink"
+        + "|pioneer|pizza|place|play|playstation|plumbing|plus|pnc|pohl|poker|politie|porn|post"
+        + "|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties"
+        + "|property|protection|pru|prudential|pub|pwc|p[aefghklmnrstwy])"
+        + "|(?:qpon|quebec|quest|qa)"
+        + "|(?:racing|radio|read|realestate|realtor|realty|recipes|red|redstone|redumbrella"
+        + "|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant"
+        + "|review|reviews|rexroth|rich|richardli|ricoh|ril|rio|rip|rocher|rocks|rodeo|rogers|room"
+        + "|rsvp|rugby|ruhr|run|rwe|ryukyu|r[eosuw])"
+        + "|(?:saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant"
+        + "|sanofi|sap|sarl|sas|save|saxo|sbi|sbs|sca|scb|schaeffler|schmidt|scholarships|school"
+        + "|schule|schwarz|science|scot|search|seat|secure|security|seek|select|sener|services"
+        + "|seven|sew|sex|sexy|sfr|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping"
+        + "|shouji|show|showtime|silk|sina|singles|site|ski|skin|sky|skype|sling|smart|smile|sncf"
+        + "|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|spa|space|sport"
+        + "|spot|srl|stada|staples|star|statebank|statefarm|stc|stcgroup|stockholm|storage|store"
+        + "|stream|studio|study|style|sucks|supplies|supply|support|surf|surgery|suzuki|swatch"
+        + "|swiss|sydney|systems|s[abcdeghijklmnorstuvxyz])"
+        + "|(?:tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tci|tdk|team|tech"
+        + "|technology|tel|temasek|tennis|teva|thd|theater|theatre|tiaa|tickets|tienda|tips|tires"
+        + "|tirol|tjmaxx|tjx|tkmaxx|tmall|today|tokyo|tools|top|toray|toshiba|total|tours|town"
+        + "|toyota|toys|trade|trading|training|travel|travelers|travelersinsurance|trust|trv|tube"
+        + "|tui|tunes|tushu|tvs|t[cdfghjklmnortvwz])"
+        + "|(?:ubank|ubs|unicom|university|uno|uol|ups|u[agksyz])"
+        + "|(?:vacations|vana|vanguard|vegas|ventures|verisign|versicherung|vet|viajes|video"
+        + "|vig|viking|villas|vin|vip|virgin|visa|vision|viva|vivo|vlaanderen|vodka|volkswagen"
+        + "|volvo|vote|voting|voto|voyage|v[aceginu])"
+        + "|(?:wales|walmart|walter|wang|wanggou|watch|watches|weather|weatherchannel|webcam"
+        + "|weber|website|wed|wedding|weibo|weir|whoswho|wien|wiki|williamhill|win|windows|wine"
+        + "|winners|wme|wolterskluwer|woodside|work|works|world|wow|wtc|wtf|w[fs])"
+        + "|(?:\u03b5\u03bb|\u03b5\u03c5|\u0431\u0433|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438"
+        + "|\u0435\u044e|\u043a\u0430\u0442\u043e\u043b\u0438\u043a|\u043a\u043e\u043c|\u043c\u043a\u0434"
         + "|\u043c\u043e\u043d|\u043c\u043e\u0441\u043a\u0432\u0430|\u043e\u043d\u043b\u0430\u0439\u043d"
         + "|\u043e\u0440\u0433|\u0440\u0443\u0441|\u0440\u0444|\u0441\u0430\u0439\u0442|\u0441\u0440\u0431"
-        + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05e7\u05d5\u05dd|\u0627\u0631\u0627\u0645\u0643\u0648"
-        + "|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
-        + "|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0627\u06cc\u0631\u0627\u0646"
-        + "|\u0628\u0627\u0632\u0627\u0631|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633"
-        + "|\u0633\u0648\u062f\u0627\u0646|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629"
-        + "|\u0639\u0631\u0627\u0642|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646"
-        + "|\u0642\u0637\u0631|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627"
-        + "|\u0645\u0648\u0642\u0639|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
-        + "|\u0938\u0902\u0917\u0920\u0928|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4"
-        + "|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
-        + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22"
-        + "|\u10d2\u10d4|\u307f\u3093\u306a|\u30b0\u30fc\u30b0\u30eb|\u30b3\u30e0|\u4e16\u754c"
-        + "|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51|\u4f01\u4e1a|\u4f5b\u5c71"
-        + "|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063"
-        + "|\u5546\u57ce|\u5546\u5e97|\u5546\u6807|\u5728\u7ebf|\u5927\u62ff|\u5a31\u4e50|\u5de5\u884c"
-        + "|\u5e7f\u4e1c|\u6148\u5584|\u6211\u7231\u4f60|\u624b\u673a|\u653f\u52a1|\u653f\u5e9c"
-        + "|\u65b0\u52a0\u5761|\u65b0\u95fb|\u65f6\u5c1a|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f"
-        + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7edc"
-        + "|\u8c37\u6b4c|\u96c6\u56e2|\u98de\u5229\u6d66|\u9910\u5385|\u9999\u6e2f|\ub2f7\ub137"
-        + "|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d|xbox"
-        + "|xerox|xin|xn\\-\\-11b4c3d|xn\\-\\-1qqw23a|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g"
-        + "|xn\\-\\-3e0b707e|xn\\-\\-3pxu8k|xn\\-\\-42c2d9a|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4gbrim"
-        + "|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks"
-        + "|xn\\-\\-80ao21a|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-90a3ac|xn\\-\\-90ais|xn\\-\\-9dbq2a"
-        + "|xn\\-\\-9et52u|xn\\-\\-b4w605ferd|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
-        + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-efvy88h"
-        + "|xn\\-\\-estv75g|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s"
-        + "|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-gecrj9c"
-        + "|xn\\-\\-h2brj9c|xn\\-\\-hxt814e|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i"
-        + "|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
+        + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05d9\u05e9\u05e8\u05d0\u05dc"
+        + "|\u05e7\u05d5\u05dd|\u0627\u0628\u0648\u0638\u0628\u064a|\u0627\u062a\u0635\u0627\u0644\u0627\u062a"
+        + "|\u0627\u0631\u0627\u0645\u0643\u0648|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u0628\u062d\u0631\u064a\u0646"
+        + "|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
+        + "|\u0627\u0644\u0639\u0644\u064a\u0627\u0646|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a"
+        + "|\u0627\u06cc\u0631\u0627\u0646|\u0628\u0627\u0631\u062a|\u0628\u0627\u0632\u0627\u0631"
+        + "|\u0628\u064a\u062a\u0643|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633|\u0633\u0648\u062f\u0627\u0646"
+        + "|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629|\u0639\u0631\u0627\u0642|\u0639\u0631\u0628"
+        + "|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646|\u0642\u0637\u0631|\u0643\u0627\u062b\u0648\u0644\u064a\u0643"
+        + "|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627|\u0645\u0648\u0631\u064a\u062a\u0627\u0646\u064a\u0627"
+        + "|\u0645\u0648\u0642\u0639|\u0647\u0645\u0631\u0627\u0647|\u067e\u0627\u06a9\u0633\u062a\u0627\u0646"
+        + "|\u0680\u0627\u0631\u062a|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
+        + "|\u092d\u093e\u0930\u0924\u092e\u094d|\u092d\u093e\u0930\u094b\u0924|\u0938\u0902\u0917\u0920\u0928"
+        + "|\u09ac\u09be\u0982\u09b2\u09be|\u09ad\u09be\u09b0\u09a4|\u09ad\u09be\u09f0\u09a4|\u0a2d\u0a3e\u0a30\u0a24"
+        + "|\u0aad\u0abe\u0ab0\u0aa4|\u0b2d\u0b3e\u0b30\u0b24|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe"
+        + "|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
+        + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0cad\u0cbe\u0cb0\u0ca4|\u0d2d\u0d3e\u0d30\u0d24\u0d02"
+        + "|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22|\u0ea5\u0eb2\u0ea7|\u10d2\u10d4"
+        + "|\u307f\u3093\u306a|\u30a2\u30de\u30be\u30f3|\u30af\u30e9\u30a6\u30c9|\u30b0\u30fc\u30b0\u30eb"
+        + "|\u30b3\u30e0|\u30b9\u30c8\u30a2|\u30bb\u30fc\u30eb|\u30d5\u30a1\u30c3\u30b7\u30e7\u30f3"
+        + "|\u30dd\u30a4\u30f3\u30c8|\u4e16\u754c|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51"
+        + "|\u4e9a\u9a6c\u900a|\u4f01\u4e1a|\u4f5b\u5c71|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366"
+        + "|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063|\u5546\u57ce|\u5546\u5e97|\u5546\u6807"
+        + "|\u5609\u91cc|\u5609\u91cc\u5927\u9152\u5e97|\u5728\u7ebf|\u5927\u62ff|\u5929\u4e3b\u6559"
+        + "|\u5a31\u4e50|\u5bb6\u96fb|\u5e7f\u4e1c|\u5fae\u535a|\u6148\u5584|\u6211\u7231\u4f60"
+        + "|\u624b\u673a|\u62db\u8058|\u653f\u52a1|\u653f\u5e9c|\u65b0\u52a0\u5761|\u65b0\u95fb"
+        + "|\u65f6\u5c1a|\u66f8\u7c4d|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f|\u6fb3\u9580"
+        + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7ad9"
+        + "|\u7f51\u7edc|\u8054\u901a|\u8c37\u6b4c|\u8d2d\u7269|\u901a\u8ca9|\u96c6\u56e2|\u96fb\u8a0a\u76c8\u79d1"
+        + "|\u98de\u5229\u6d66|\u98df\u54c1|\u9910\u5385|\u9999\u683c\u91cc\u62c9|\u9999\u6e2f"
+        + "|\ub2f7\ub137|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d"
+        + "|xbox|xerox|xfinity|xihuan|xin|xn\\-\\-11b4c3d|xn\\-\\-1ck2e1b|xn\\-\\-1qqw23a|xn\\-\\-2scrj9c"
+        + "|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g|xn\\-\\-3e0b707e|xn\\-\\-3hcrj9c|xn\\-\\-3pxu8k"
+        + "|xn\\-\\-42c2d9a|xn\\-\\-45br5cyl|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4dbrk0ce|xn\\-\\-4gbrim"
+        + "|xn\\-\\-54b7fta0cc|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-5su34j936bgsg|xn\\-\\-5tzm5g"
+        + "|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks|xn\\-\\-80ao21a|xn\\-\\-80aqecdr1a"
+        + "|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-8y0a063a|xn\\-\\-90a3ac|xn\\-\\-90ae|xn\\-\\-90ais"
+        + "|xn\\-\\-9dbq2a|xn\\-\\-9et52u|xn\\-\\-9krt00a|xn\\-\\-b4w605ferd|xn\\-\\-bck1b9a5dre4c"
+        + "|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cck2b3b|xn\\-\\-cckwcxetd|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
+        + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-e1a4c"
+        + "|xn\\-\\-eckvdtc9d|xn\\-\\-efvy88h|xn\\-\\-fct429k|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs"
+        + "|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d"
+        + "|xn\\-\\-fzc2c9e2c|xn\\-\\-fzys8d69uvgm|xn\\-\\-g2xx48c|xn\\-\\-gckr3f0f|xn\\-\\-gecrj9c"
+        + "|xn\\-\\-gk3at1e|xn\\-\\-h2breg3eve|xn\\-\\-h2brj9c|xn\\-\\-h2brj9c8c|xn\\-\\-hxt814e"
+        + "|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g"
+        + "|xn\\-\\-jlq480n2rg|xn\\-\\-jvr189m|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
         + "|xn\\-\\-kput3i|xn\\-\\-l1acc|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgb9awbf|xn\\-\\-mgba3a3ejt"
-        + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e"
-        + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbpl2fh|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
-        + "|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema"
-        + "|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh"
-        + "|xn\\-\\-pssy2u|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-s9brj9c"
-        + "|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb"
-        + "|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv|xn\\-\\-vuq861b|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
+        + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgba7c0bbn0a|xn\\-\\-mgbaakc7dvf|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd"
+        + "|xn\\-\\-mgbah1a3hjkrd|xn\\-\\-mgbai9azgqp6j|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a|xn\\-\\-mgbbh1a71e"
+        + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgbca7dzdo|xn\\-\\-mgbcpq6gpa1a|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbgu82a"
+        + "|xn\\-\\-mgbi4ecexp|xn\\-\\-mgbpl2fh|xn\\-\\-mgbt3dhd|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
+        + "|xn\\-\\-mix891f|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-ngbe9e0a|xn\\-\\-ngbrx"
+        + "|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl"
+        + "|xn\\-\\-otu796d|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-pssy2u|xn\\-\\-q7ce6a"
+        + "|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxa6a|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-rovu88b"
+        + "|xn\\-\\-rvc1e0am3e|xn\\-\\-s9brj9c|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-tiq49xqyj"
+        + "|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv"
+        + "|xn\\-\\-vuq861b|xn\\-\\-w4r85el8fhu5dnra|xn\\-\\-w4rs40l|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
         + "|xn\\-\\-xhq521b|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-y9a3aq|xn\\-\\-yfro4i67o"
-        + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xperia|xxx|xyz)"
-        + "|(?:yachts|yamaxun|yandex|yodobashi|yoga|yokohama|youtube|y[et])"
-        + "|(?:zara|zip|zone|zuerich|z[amw]))";
-
+        + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xxx|xyz)"
+        + "|(?:yachts|yahoo|yamaxun|yandex|yodobashi|yoga|yokohama|you|youtube|yun|y[et])"
+        + "|(?:zappos|zara|zero|zip|zone|zuerich|z[amw]))";
     /**
      * Kept for backward compatibility reasons.
      *
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index bc83750..2761aae 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -35,8 +35,6 @@
  * @hide
  */
 public final class InputWindowHandle {
-    // TODO (b/300094445): Convert to use correct flagging infrastructure
-    public static final boolean USE_SURFACE_TRUSTED_OVERLAY = true;
 
     /**
      * An internal annotation for all the {@link android.os.InputConfig} flags that can be
@@ -61,6 +59,7 @@
             InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
             InputConfig.IS_WALLPAPER,
             InputConfig.PAUSE_DISPATCHING,
+            InputConfig.TRUSTED_OVERLAY,
             InputConfig.WATCH_OUTSIDE_TOUCH,
             InputConfig.SLIPPERY,
             InputConfig.DISABLE_USER_ACTIVITY,
@@ -273,13 +272,4 @@
         }
         this.inputConfig &= ~inputConfig;
     }
-
-    public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
-            boolean isTrusted) {
-        if (USE_SURFACE_TRUSTED_OVERLAY) {
-            t.setTrustedOverlay(sc, isTrusted);
-        } else if (isTrusted) {
-            inputConfig |= InputConfig.TRUSTED_OVERLAY;
-        }
-    }
 }
diff --git a/core/java/android/view/KeyCharacterMap.aidl b/core/java/android/view/KeyCharacterMap.aidl
new file mode 100644
index 0000000..1a761a67
--- /dev/null
+++ b/core/java/android/view/KeyCharacterMap.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.view;
+
+parcelable KeyCharacterMap;
\ No newline at end of file
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index d8221a6..4fe53c2 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.input.InputManagerGlobal;
@@ -309,6 +310,10 @@
     private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
     private static native boolean nativeEquals(long ptr1, long ptr2);
 
+    private static native void nativeApplyOverlay(long ptr, String layoutDescriptor,
+            String overlay);
+    private static native int nativeGetMappedKey(long ptr, int scanCode);
+
     private KeyCharacterMap(Parcel in) {
         if (in == null) {
             throw new IllegalArgumentException("parcel must not be null");
@@ -368,6 +373,38 @@
     }
 
     /**
+     * Loads the key character map with applied KCM overlay.
+     *
+     * @param layoutDescriptor descriptor of the applied overlay KCM
+     * @param overlay          string describing the overlay KCM
+     * @return The resultant key character map.
+     * @throws {@link UnavailableException} if the key character map
+     *                could not be loaded because it was malformed or the default key character map
+     *                is missing from the system.
+     * @hide
+     */
+    public static KeyCharacterMap load(@NonNull String layoutDescriptor, @NonNull String overlay) {
+        KeyCharacterMap kcm = KeyCharacterMap.load(VIRTUAL_KEYBOARD);
+        kcm.applyOverlay(layoutDescriptor, overlay);
+        return kcm;
+    }
+
+    private void applyOverlay(@NonNull String layoutDescriptor, @NonNull String overlay) {
+        nativeApplyOverlay(mPtr, layoutDescriptor, overlay);
+    }
+
+    /**
+     * Gets the mapped key for the provided scan code. Returns the provided default if no mapping
+     * found in the KeyCharacterMap.
+     *
+     * @hide
+     */
+    public int getMappedKeyOrDefault(int scanCode, int defaultKeyCode) {
+        int keyCode = nativeGetMappedKey(mPtr, scanCode);
+        return keyCode == KeyEvent.KEYCODE_UNKNOWN ? defaultKeyCode : keyCode;
+    }
+
+    /**
      * Gets the Unicode character generated by the specified key and meta
      * key state combination.
      * <p>
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 30fd2cf..4d53b2c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1584,11 +1584,11 @@
      */
     static final int DISABLED = 0x00000020;
 
-   /**
-    * Mask for use with setFlags indicating bits used for indicating whether
-    * this view is enabled
-    * {@hide}
-    */
+    /**
+     * Mask for use with setFlags indicating bits used for indicating whether
+     * this view is enabled
+     * {@hide}
+     */
     static final int ENABLED_MASK = 0x00000020;
 
     /**
@@ -15251,13 +15251,13 @@
       }
     }
 
-   /**
-    * @see #performAccessibilityAction(int, Bundle)
-    *
-    * Note: Called from the default {@link AccessibilityDelegate}.
-    *
-    * @hide
-    */
+    /**
+     * @see #performAccessibilityAction(int, Bundle)
+     *
+     * Note: Called from the default {@link AccessibilityDelegate}.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public boolean performAccessibilityActionInternal(int action, @Nullable Bundle arguments) {
         if (isNestedScrollingEnabled()
@@ -17391,7 +17391,7 @@
         return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress);
     }
 
-   /**
+    /**
      * Remove the pending click action
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 85d7c10..5720fc0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3752,7 +3752,15 @@
                         && !child.isActivityDeniedForAutofillForUnimportantView())
                     || (shouldIncludeAllChildrenViewWithAutofillTypeNotNone(afm)
                         && child.getAutofillType() != AUTOFILL_TYPE_NONE)
-                    || shouldIncludeAllChildrenViews(afm)){
+                    || shouldIncludeAllChildrenViews(afm)
+                    || (child instanceof ViewGroup && child.getVisibility() != View.VISIBLE)) {
+                // If the child is a ViewGroup object and its visibility is not visible, include
+                // it as part of the assist structure. The children of these invisible ViewGroup
+                // objects are parsed and included in the assist structure. When the Autofill
+                // Provider determines the visibility of these children, it looks at their
+                // visibility as well as their parent's visibility. Omitting invisible parents
+                // will lead to the Autofill Provider incorrectly assuming that these children
+                // of invisible parents are actually visible.
                 list.add(child);
             } else if (child instanceof ViewGroup) {
                 ((ViewGroup) child).populateChildrenForAutofill(list, flags);
@@ -4437,7 +4445,7 @@
      * @param drawingPosition the drawing order position.
      * @return the container position of a child for this drawing order position.
      *
-     * @see #getChildDrawingOrder(int, int)}
+     * @see #getChildDrawingOrder(int, int)
      */
     public final int getChildDrawingOrder(int drawingPosition) {
         return getChildDrawingOrder(getChildCount(), drawingPosition);
@@ -7404,19 +7412,26 @@
                 }
                 target = next;
             }
-            if (!childIsHit) {
+            if (!childIsHit && mFirstHoverTarget != null) {
                 target = mFirstHoverTarget;
+                final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                 while (notEmpty && target != null) {
                     final HoverTarget next = target.next;
                     final View hoveredView = target.child;
 
-                    rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
-                            hoveredView.mBottom);
-                    matrix.mapRect(rect);
-                    notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
-                            Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+                    if (!isOnTop(child, hoveredView, preorderedList)) {
+                        rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
+                                hoveredView.mBottom);
+                        matrix.mapRect(rect);
+                        notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+                                Math.round(rect.right), Math.round(rect.bottom),
+                                Region.Op.DIFFERENCE);
+                    }
                     target = next;
                 }
+                if (preorderedList != null) {
+                    preorderedList.clear();
+                }
             }
         } else {
             TouchTarget target = mFirstTouchTarget;
@@ -7429,19 +7444,26 @@
                 }
                 target = next;
             }
-            if (!childIsHit) {
+            if (!childIsHit && mFirstTouchTarget != null) {
                 target = mFirstTouchTarget;
+                final ArrayList<View> preorderedList = buildOrderedChildList();
                 while (notEmpty && target != null) {
                     final TouchTarget next = target.next;
                     final View touchedView = target.child;
 
-                    rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
-                            touchedView.mBottom);
-                    matrix.mapRect(rect);
-                    notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
-                            Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+                    if (!isOnTop(child, touchedView, preorderedList)) {
+                        rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
+                                touchedView.mBottom);
+                        matrix.mapRect(rect);
+                        notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+                                Math.round(rect.right), Math.round(rect.bottom),
+                                Region.Op.DIFFERENCE);
+                    }
                     target = next;
                 }
+                if (preorderedList != null) {
+                    preorderedList.clear();
+                }
             }
         }
 
@@ -7451,6 +7473,28 @@
         return notEmpty;
     }
 
+    /**
+     * Return true if the given {@code view} is drawn on top of the {@code otherView}.
+     * Both the {@code view} and {@code otherView} must be children of this ViewGroup.
+     * Otherwise, the returned value is meaningless.
+     */
+    private boolean isOnTop(View view, View otherView, ArrayList<View> preorderedList) {
+        final int childrenCount = mChildrenCount;
+        final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
+        final View[] children = mChildren;
+        for (int i = childrenCount - 1; i >= 0; i--) {
+            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+            if (child == view) {
+                return true;
+            }
+            if (child == otherView) {
+                return false;
+            }
+        }
+        // Can't find the view and otherView in the children list. Return value is meaningless.
+        return false;
+    }
 
     private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) {
         final int[] locationInWindow = new int[2];
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d191ccd..ff6165b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -461,6 +461,9 @@
     @NonNull Display mDisplay;
     final String mBasePackageName;
 
+    // If we would like to keep a particular eye on the corresponding package.
+    final boolean mExtraDisplayListenerLogging;
+
     final int[] mTmpLocation = new int[2];
 
     final TypedValue mTmpValue = new TypedValue();
@@ -1010,6 +1013,8 @@
         mWindowLayout = windowLayout;
         mDisplay = display;
         mBasePackageName = context.getBasePackageName();
+        final String name = DisplayProperties.debug_vri_package().orElse(null);
+        mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
         mThread = Thread.currentThread();
         mLocation = new WindowLeaked(null);
         mLocation.fillInStackTrace();
@@ -1444,6 +1449,10 @@
                 // We should update mAttachInfo.mDisplayState after registerDisplayListener
                 // because displayState might be changed before registerDisplayListener.
                 mAttachInfo.mDisplayState = mDisplay.getState();
+                if (mExtraDisplayListenerLogging) {
+                    Slog.i(mTag, "(" + mBasePackageName + ") Initial DisplayState: "
+                            + mAttachInfo.mDisplayState, new Throwable());
+                }
                 if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
                     mUseBLASTAdapter = true;
                 }
@@ -1528,6 +1537,9 @@
      * Register any kind of listeners if setView was success.
      */
     private void registerListeners() {
+        if (mExtraDisplayListenerLogging) {
+            Slog.i(mTag, "Register listeners: " + mBasePackageName);
+        }
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityInteractionConnectionManager, mHandler);
         mAccessibilityManager.addHighTextContrastStateChangeListener(
@@ -1553,6 +1565,9 @@
         DisplayManagerGlobal
                 .getInstance()
                 .unregisterDisplayListener(mDisplayListener);
+        if (mExtraDisplayListenerLogging) {
+            Slog.w(mTag, "Unregister listeners: " + mBasePackageName, new Throwable());
+        }
     }
 
     private void setTag() {
@@ -1960,9 +1975,16 @@
     private final DisplayListener mDisplayListener = new DisplayListener() {
         @Override
         public void onDisplayChanged(int displayId) {
+            if (mExtraDisplayListenerLogging) {
+                Slog.i(mTag, "Received onDisplayChanged - " + mView);
+            }
             if (mView != null && mDisplay.getDisplayId() == displayId) {
                 final int oldDisplayState = mAttachInfo.mDisplayState;
                 final int newDisplayState = mDisplay.getState();
+                if (mExtraDisplayListenerLogging) {
+                    Slog.i(mTag, "DisplayState - old: " + oldDisplayState
+                            + ", new: " + newDisplayState);
+                }
                 if (oldDisplayState != newDisplayState) {
                     mAttachInfo.mDisplayState = newDisplayState;
                     pokeDrawLockIfNeeded();
@@ -3995,8 +4017,6 @@
             mWindowFocusChanged = false;
             hasWindowFocus = mUpcomingWindowFocus;
         }
-        // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
-        // config changes.
         if (hasWindowFocus) {
             mInsetsController.onWindowFocusGained(
                     getFocusedViewOrNull() != null /* hasViewFocused */);
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 3b8298e..dda3993 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -124,16 +124,16 @@
                     || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                 final Insets systemBarsInsets = state.calculateInsets(
                         displayFrame, systemBars(), requestedVisibleTypes);
-                if (systemBarsInsets.left > 0) {
+                if (systemBarsInsets.left >= cutout.getSafeInsetLeft()) {
                     displayCutoutSafeExceptMaybeBars.left = MIN_X;
                 }
-                if (systemBarsInsets.top > 0) {
+                if (systemBarsInsets.top >= cutout.getSafeInsetTop()) {
                     displayCutoutSafeExceptMaybeBars.top = MIN_Y;
                 }
-                if (systemBarsInsets.right > 0) {
+                if (systemBarsInsets.right >= cutout.getSafeInsetRight()) {
                     displayCutoutSafeExceptMaybeBars.right = MAX_X;
                 }
-                if (systemBarsInsets.bottom > 0) {
+                if (systemBarsInsets.bottom >= cutout.getSafeInsetBottom()) {
                     displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
                 }
             }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 432f6e1..4cb8788 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1474,7 +1474,7 @@
         }
         for (int i = 0; i < infos.size(); i++) {
             final VirtualViewFillInfo info = infos.valueAt(i);
-            final int virtualId = infos.indexOfKey(i);
+            final int virtualId = infos.keyAt(i);
             notifyViewReadyInner(getAutofillId(view, virtualId),
                     (info == null) ? null : info.getAutofillHints());
         }
@@ -1488,9 +1488,6 @@
      * @hide
      */
     public void notifyViewEnteredForFillDialog(View v) {
-        if (sDebug) {
-            Log.d(TAG, "notifyViewEnteredForFillDialog:" + v.getAutofillId());
-        }
         if (v.isCredential()
                 && mIsFillAndSaveDialogDisabledForCredentialManager) {
             if (sDebug) {
@@ -1503,11 +1500,14 @@
         notifyViewReadyInner(v.getAutofillId(), v.getAutofillHints());
     }
 
-    private void notifyViewReadyInner(AutofillId id, String[] autofillHints) {
+    private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints) {
+        if (sDebug) {
+            Log.d(TAG, "notifyViewReadyInner:" + id);
+        }
+
         if (!hasAutofillFeature()) {
             return;
         }
-
         synchronized (mLock) {
             if (mAllTrackedViews.contains(id)) {
                 // The id is tracked and will not trigger pre-fill request again.
@@ -1543,26 +1543,38 @@
                     final boolean clientAdded = tryAddServiceClientIfNeededLocked();
                     if (clientAdded) {
                         startSessionLocked(/* id= */ AutofillId.NO_AUTOFILL_ID, /* bounds= */ null,
-                                /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
+                            /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
                     } else {
                         if (sVerbose) {
                             Log.v(TAG, "not starting session: no service client");
                         }
                     }
-
                 }
             }
         }
 
-        if (mIsFillDialogEnabled
-                || ArrayUtils.containsAny(autofillHints, mFillDialogEnabledHints)) {
+        // Check if framework should send pre-fill request for fill dialog
+        boolean shouldSendPreFillRequestForFillDialog = false;
+        if (mIsFillDialogEnabled) {
+            shouldSendPreFillRequestForFillDialog = true;
+        } else if (autofillHints != null) {
+            // check if supported autofill hint is present
+            for (String autofillHint : autofillHints) {
+                for (String filldialogEnabledHint : mFillDialogEnabledHints) {
+                    if (filldialogEnabledHint.equalsIgnoreCase(autofillHint)) {
+                        shouldSendPreFillRequestForFillDialog = true;
+                        break;
+                    }
+                }
+                if (shouldSendPreFillRequestForFillDialog) break;
+            }
+        }
+        if (shouldSendPreFillRequestForFillDialog) {
             if (sDebug) {
                 Log.d(TAG, "Triggering pre-emptive request for fill dialog.");
             }
-
             int flags = FLAG_SUPPORTS_FILL_DIALOG;
             flags |= FLAG_VIEW_NOT_FOCUSED;
-
             synchronized (mLock) {
                 // To match the id of the IME served view, used AutofillId.NO_AUTOFILL_ID on prefill
                 // request, because IME will reset the id of IME served view to 0 when activity
@@ -1570,9 +1582,10 @@
                 // not match the IME served view's, Autofill will be blocking to wait inline
                 // request from the IME.
                 notifyViewEnteredLocked(/* view= */ null, AutofillId.NO_AUTOFILL_ID,
-                        /* bounds= */ null,  /* value= */ null, flags);
+                    /* bounds= */ null,  /* value= */ null, flags);
             }
         }
+        return;
     }
 
     private boolean hasFillDialogUiFeature() {
diff --git a/core/java/android/view/inputmethod/TEST_MAPPING b/core/java/android/view/inputmethod/TEST_MAPPING
index 4b2ea1a..ad59463 100644
--- a/core/java/android/view/inputmethod/TEST_MAPPING
+++ b/core/java/android/view/inputmethod/TEST_MAPPING
@@ -11,6 +11,9 @@
         },
         {
           "exclude-annotation": "android.platform.test.annotations.AppModeFull"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.LargeTest"
         }
       ]
     }
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 92d3408..c144289 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -2,7 +2,8 @@
 
 flag {
     name: "refactor_insets_controller"
-    namespace: "inputmethod"
+    namespace: "input_method"
     description: "Feature flag for refactoring InsetsController and removing ImeInsetsSourceConsumer"
     bug: "298172246"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 065089f5..53c73c6 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -16,10 +16,7 @@
 
 package android.widget;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.TypedArray;
 import android.os.Message;
 import android.util.AttributeSet;
@@ -48,7 +45,6 @@
     private boolean mRunning = false;
     private boolean mStarted = false;
     private boolean mVisible = false;
-    private boolean mUserPresent = true;
     private boolean mAdvancedByHost = false;
 
     public AdapterViewFlipper(Context context) {
@@ -82,40 +78,10 @@
         a.recycle();
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                mUserPresent = false;
-                updateRunning();
-            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                mUserPresent = true;
-                updateRunning(false);
-            }
-        }
-    };
-
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        // Listen for broadcasts related to user-presence
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
-
-        // OK, this is gross but needed. This class is supported by the
-        // remote views machanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For exmaple, when adding widgets from a user profile to the
-        // home screen. Therefore, we register the receiver as the current
-        // user not the one the context is for.
-        getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
-                filter, null, getHandler());
-
-
         if (mAutoStart) {
             // Automatically start when requested
             startFlipping();
@@ -126,8 +92,6 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mVisible = false;
-
-        getContext().unregisterReceiver(mReceiver);
         updateRunning();
     }
 
@@ -235,8 +199,7 @@
      *            true.
      */
     private void updateRunning(boolean flipNow) {
-        boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
-                && mAdapter != null;
+        boolean running = !mAdvancedByHost && mVisible && mStarted && mAdapter != null;
         if (running != mRunning) {
             if (running) {
                 showOnly(mWhichChild, flipNow);
@@ -248,7 +211,7 @@
         }
         if (LOGD) {
             Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
-                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+                    + ", mRunning=" + mRunning);
         }
     }
 
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1f0e95e..e0158332 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -23,7 +23,6 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.BlendMode;
@@ -37,6 +36,9 @@
 import android.view.View;
 import android.view.inspector.InspectableProperty;
 import android.widget.RemoteViews.RemoteView;
+import android.widget.TextClock.ClockEventDelegate;
+
+import com.android.internal.util.Preconditions;
 
 import java.time.Clock;
 import java.time.DateTimeException;
@@ -112,6 +114,7 @@
     public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
+        mClockEventDelegate = new ClockEventDelegate(context);
         mSecondsHandFps = AppGlobals.getIntCoreSetting(
                 WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS,
                 context.getResources()
@@ -584,21 +587,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        IntentFilter filter = new IntentFilter();
 
         if (!mReceiverAttached) {
-            filter.addAction(Intent.ACTION_TIME_CHANGED);
-            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
-            // OK, this is gross but needed. This class is supported by the
-            // remote views mechanism and as a part of that the remote views
-            // can be inflated by a context for another user without the app
-            // having interact users permission - just for loading resources.
-            // For example, when adding widgets from a user profile to the
-            // home screen. Therefore, we register the receiver as the current
-            // user not the one the context is for.
-            getContext().registerReceiverAsUser(mIntentReceiver,
-                    android.os.Process.myUserHandle(), filter, null, getHandler());
+            mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
             mReceiverAttached = true;
         }
 
@@ -615,12 +606,23 @@
     @Override
     protected void onDetachedFromWindow() {
         if (mReceiverAttached) {
-            getContext().unregisterReceiver(mIntentReceiver);
+            mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
             mReceiverAttached = false;
         }
         super.onDetachedFromWindow();
     }
 
+    /**
+     * Sets a delegate to handle clock event registration. This must be called before the view is
+     * attached to the window
+     *
+     * @hide
+     */
+    public void setClockEventDelegate(ClockEventDelegate delegate) {
+        Preconditions.checkState(!mReceiverAttached, "Clock events already registered");
+        mClockEventDelegate = delegate;
+    }
+
     private void onVisible() {
         if (!mVisible) {
             mVisible = true;
@@ -797,6 +799,7 @@
         }
     };
     private boolean mReceiverAttached;
+    private ClockEventDelegate mClockEventDelegate;
 
     private final Runnable mTick = new Runnable() {
         @Override
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index e48afb2..255bd67 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import static android.os.Process.myUserHandle;
 import static android.view.ViewDebug.ExportedProperty;
 import static android.widget.RemoteViews.RemoteView;
 
@@ -24,7 +25,6 @@
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -43,6 +43,7 @@
 import android.view.inspector.InspectableProperty;
 
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 
 import java.time.Duration;
 import java.time.Instant;
@@ -141,6 +142,8 @@
     private boolean mRegistered;
     private boolean mShouldRunTicker;
 
+    private ClockEventDelegate mClockEventDelegate;
+
     private Calendar mTime;
     private String mTimeZone;
 
@@ -178,8 +181,7 @@
             if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
                 final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
                 createTime(timeZone);
-            } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
-                    || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
+            } else if (!mShouldRunTicker && Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
                 return;
             }
             onTimeChanged();
@@ -282,6 +284,7 @@
         if (mFormat24 == null) {
             mFormat24 = getBestDateTimePattern("Hm");
         }
+        mClockEventDelegate = new ClockEventDelegate(getContext());
 
         createTime(mTimeZone);
         chooseFormat();
@@ -431,6 +434,17 @@
     }
 
     /**
+     * Sets a delegate to handle clock event registration. This must be called before the view is
+     * attached to the window
+     *
+     * @hide
+     */
+    public void setClockEventDelegate(ClockEventDelegate delegate) {
+        Preconditions.checkState(!mRegistered, "Clock events already registered");
+        mClockEventDelegate = delegate;
+    }
+
+    /**
      * Update the displayed time if necessary and invalidate the view.
      */
     public void refreshTime() {
@@ -557,7 +571,7 @@
         if (!mRegistered) {
             mRegistered = true;
 
-            registerReceiver();
+            mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
             registerObserver();
 
             createTime(mTimeZone);
@@ -582,7 +596,7 @@
         super.onDetachedFromWindow();
 
         if (mRegistered) {
-            unregisterReceiver();
+            mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
             unregisterObserver();
 
             mRegistered = false;
@@ -598,56 +612,27 @@
         mStopTicking = true;
     }
 
-    private void registerReceiver() {
-        final IntentFilter filter = new IntentFilter();
-
-        filter.addAction(Intent.ACTION_TIME_CHANGED);
-        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
-        // OK, this is gross but needed. This class is supported by the
-        // remote views mechanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For example, when adding widgets from a managed profile to the
-        // home screen. Therefore, we register the receiver as the user
-        // the app is running as not the one the context is for.
-        getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
-                filter, null, getHandler());
-    }
-
     private void registerObserver() {
         if (mRegistered) {
             if (mFormatChangeObserver == null) {
                 mFormatChangeObserver = new FormatChangeObserver(getHandler());
             }
-            final ContentResolver resolver = getContext().getContentResolver();
-            Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
-            if (mShowCurrentUserTime) {
-                resolver.registerContentObserver(uri, true,
-                        mFormatChangeObserver, UserHandle.USER_ALL);
-            } else {
-                // UserHandle.myUserId() is needed. This class is supported by the
-                // remote views mechanism and as a part of that the remote views
-                // can be inflated by a context for another user without the app
-                // having interact users permission - just for loading resources.
-                // For example, when adding widgets from a managed profile to the
-                // home screen. Therefore, we register the ContentObserver with the user
-                // the app is running (e.g. the launcher) and not the user of the
-                // context (e.g. the widget's profile).
-                resolver.registerContentObserver(uri, true,
-                        mFormatChangeObserver, UserHandle.myUserId());
-            }
+            // UserHandle.myUserId() is needed. This class is supported by the
+            // remote views mechanism and as a part of that the remote views
+            // can be inflated by a context for another user without the app
+            // having interact users permission - just for loading resources.
+            // For example, when adding widgets from a managed profile to the
+            // home screen. Therefore, we register the ContentObserver with the user
+            // the app is running (e.g. the launcher) and not the user of the
+            // context (e.g. the widget's profile).
+            int userHandle = mShowCurrentUserTime ? UserHandle.USER_ALL : UserHandle.myUserId();
+            mClockEventDelegate.registerFormatChangeObserver(mFormatChangeObserver, userHandle);
         }
     }
 
-    private void unregisterReceiver() {
-        getContext().unregisterReceiver(mIntentReceiver);
-    }
-
     private void unregisterObserver() {
         if (mFormatChangeObserver != null) {
-            final ContentResolver resolver = getContext().getContentResolver();
-            resolver.unregisterContentObserver(mFormatChangeObserver);
+            mClockEventDelegate.unregisterFormatChangeObserver(mFormatChangeObserver);
         }
     }
 
@@ -674,4 +659,59 @@
         stream.addProperty("format", mFormat == null ? null : mFormat.toString());
         stream.addProperty("hasSeconds", mHasSeconds);
     }
+
+    /**
+     * Utility class to delegate some system event handling to allow overring the default behavior
+     *
+     * @hide
+     */
+    public static class ClockEventDelegate {
+
+        private final Context mContext;
+
+        public ClockEventDelegate(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Registers a receiver for actions {@link Intent#ACTION_TIME_CHANGED} and
+         * {@link Intent#ACTION_TIMEZONE_CHANGED}
+         *
+         * OK, this is gross but needed. This class is supported by the remote views mechanism and
+         * as a part of that the remote views can be inflated by a context for another user without
+         * the app having interact users permission - just for loading resources. For example,
+         * when adding widgets from a managed profile to the home screen. Therefore, we register
+         * the receiver as the user the app is running as not the one the context is for.
+         */
+        public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) {
+            final IntentFilter filter = new IntentFilter();
+
+            filter.addAction(Intent.ACTION_TIME_CHANGED);
+            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+
+            mContext.registerReceiverAsUser(receiver, myUserHandle(), filter, null, handler);
+        }
+
+        /**
+         * Unregisters a previously registered receiver
+         */
+        public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) {
+            mContext.unregisterReceiver(receiver);
+        }
+
+        /**
+         * Registers an observer for time format changes
+         */
+        public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
+            Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
+            mContext.getContentResolver().registerContentObserver(uri, true, observer, userHandle);
+        }
+
+        /**
+         * Unregisters a previously registered observer
+         */
+        public void unregisterFormatChangeObserver(ContentObserver observer) {
+            mContext.getContentResolver().unregisterContentObserver(observer);
+        }
+    }
 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 59344b0..a0d0656 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,6 +27,7 @@
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
 import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 
 import android.R;
@@ -240,7 +241,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastMath;
 import com.android.internal.util.Preconditions;
-import com.android.text.flags.Flags;
 
 import libcore.util.EmptyArray;
 
@@ -1634,7 +1634,7 @@
         }
 
         if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
-            mUseBoundsForWidth = Flags.useBoundsForWidth();
+            mUseBoundsForWidth = false;  // TODO: Connect to the flag.
         } else {
             mUseBoundsForWidth = false;
         }
@@ -14151,7 +14151,8 @@
                             selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR);
                     final int line = layout.getLineForOffset(offsetTransformed);
                     final float insertionMarkerX =
-                            layout.getPrimaryHorizontal(offsetTransformed)
+                            layout.getPrimaryHorizontal(
+                                            offsetTransformed, layout.shouldClampCursor(line))
                                     + viewportToContentHorizontalOffset;
                     final float insertionMarkerTop = layout.getLineTop(line)
                             + viewportToContentVerticalOffset;
@@ -15143,6 +15144,9 @@
 
         final ClipDescription description =
                 getClipboardManagerForUser().getPrimaryClipDescription();
+        if (description == null) {
+            return false;
+        }
         final boolean isPlainType = description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
         return (isPlainType && description.isStyledText())
                 || description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML);
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 5abb6e1..eaf037e 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -18,10 +18,7 @@
 
 import android.annotation.IntRange;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.Message;
@@ -51,8 +48,6 @@
     private boolean mRunning = false;
     private boolean mStarted = false;
     private boolean mVisible = false;
-    @UnsupportedAppUsage
-    private boolean mUserPresent = true;
 
     public ViewFlipper(Context context) {
         super(context);
@@ -70,39 +65,10 @@
         a.recycle();
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                mUserPresent = false;
-                updateRunning();
-            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                mUserPresent = true;
-                updateRunning(false);
-            }
-        }
-    };
-
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        // Listen for broadcasts related to user-presence
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
-
-        // OK, this is gross but needed. This class is supported by the
-        // remote views machanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For exmaple, when adding widgets from a user profile to the
-        // home screen. Therefore, we register the receiver as the current
-        // user not the one the context is for.
-        getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
-                filter, null, getHandler());
-
         if (mAutoStart) {
             // Automatically start when requested
             startFlipping();
@@ -113,8 +79,6 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mVisible = false;
-
-        getContext().unregisterReceiver(mReceiver);
         updateRunning();
     }
 
@@ -186,7 +150,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void updateRunning(boolean flipNow) {
-        boolean running = mVisible && mStarted && mUserPresent;
+        boolean running = mVisible && mStarted;
         if (running != mRunning) {
             if (running) {
                 showOnly(mWhichChild, flipNow);
@@ -198,7 +162,7 @@
         }
         if (LOGD) {
             Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
-                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+                    + ", mRunning=" + mRunning);
         }
     }
 
diff --git a/core/java/android/widget/inline/TEST_MAPPING b/core/java/android/widget/inline/TEST_MAPPING
index 26a5569..82c6f61 100644
--- a/core/java/android/widget/inline/TEST_MAPPING
+++ b/core/java/android/widget/inline/TEST_MAPPING
@@ -8,6 +8,9 @@
         },
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.LargeTest"
         }
       ]
     }
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 95e9e86..e42193d 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -24,7 +24,6 @@
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.os.Build;
-import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -35,7 +34,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.function.ObjIntConsumer;
-
 /**
  * Handles display and layer captures for the system.
  *
@@ -45,8 +43,6 @@
     private static final String TAG = "ScreenCapture";
     private static final int SCREENSHOT_WAIT_TIME_S = 4 * Build.HW_TIMEOUT_MULTIPLIER;
 
-    private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
-            long captureListener);
     private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
             long captureListener);
     private static native long nativeCreateScreenCaptureListener(
@@ -56,37 +52,6 @@
     private static native long getNativeListenerFinalizer();
 
     /**
-     * @param captureArgs     Arguments about how to take the screenshot
-     * @param captureListener A listener to receive the screenshot callback
-     * @hide
-     */
-    public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
-            @NonNull ScreenCaptureListener captureListener) {
-        return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
-    }
-
-    /**
-     * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
-     * the content.
-     *
-     * @hide
-     */
-    public static ScreenshotHardwareBuffer captureDisplay(
-            DisplayCaptureArgs captureArgs) {
-        SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
-        int status = captureDisplay(captureArgs, syncScreenCapture);
-        if (status != 0) {
-            return null;
-        }
-
-        try {
-            return syncScreenCapture.getBuffer();
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    /**
      * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
      *
      * @param layer      The root layer to capture.
@@ -519,92 +484,6 @@
     }
 
     /**
-     * The arguments class used to make display capture requests.
-     *
-     * @hide
-     * @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
-     */
-    public static class DisplayCaptureArgs extends CaptureArgs {
-        private final IBinder mDisplayToken;
-        private final int mWidth;
-        private final int mHeight;
-        private final boolean mUseIdentityTransform;
-
-        private DisplayCaptureArgs(Builder builder) {
-            super(builder);
-            mDisplayToken = builder.mDisplayToken;
-            mWidth = builder.mWidth;
-            mHeight = builder.mHeight;
-            mUseIdentityTransform = builder.mUseIdentityTransform;
-        }
-
-        /**
-         * The Builder class used to construct {@link DisplayCaptureArgs}
-         */
-        public static class Builder extends CaptureArgs.Builder<Builder> {
-            private IBinder mDisplayToken;
-            private int mWidth;
-            private int mHeight;
-            private boolean mUseIdentityTransform;
-
-            /**
-             * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
-             * remains valid.
-             */
-            public DisplayCaptureArgs build() {
-                if (mDisplayToken == null) {
-                    throw new IllegalStateException(
-                            "Can't take screenshot with null display token");
-                }
-                return new DisplayCaptureArgs(this);
-            }
-
-            public Builder(IBinder displayToken) {
-                setDisplayToken(displayToken);
-            }
-
-            /**
-             * The display to take the screenshot of.
-             */
-            public Builder setDisplayToken(IBinder displayToken) {
-                mDisplayToken = displayToken;
-                return this;
-            }
-
-            /**
-             * Set the desired size of the returned buffer. The raw screen  will be  scaled down to
-             * this size
-             *
-             * @param width  The desired width of the returned buffer. Caller may pass in 0 if no
-             *               scaling is desired.
-             * @param height The desired height of the returned buffer. Caller may pass in 0 if no
-             *               scaling is desired.
-             */
-            public Builder setSize(int width, int height) {
-                mWidth = width;
-                mHeight = height;
-                return this;
-            }
-
-            /**
-             * Replace the rotation transform of the display with the identity transformation while
-             * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
-             * orientation. Set this value to false if the screenshot should be taken in the
-             * current screen orientation.
-             */
-            public Builder setUseIdentityTransform(boolean useIdentityTransform) {
-                mUseIdentityTransform = useIdentityTransform;
-                return this;
-            }
-
-            @Override
-            Builder getThis() {
-                return this;
-            }
-        }
-    }
-
-    /**
      * The arguments class used to make layer capture requests.
      *
      * @hide
@@ -682,7 +561,6 @@
 
     /**
      * The object used to receive the results when invoking screen capture requests via
-     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
      * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
      *
      * This listener can only be used for a single call to capture content call.
@@ -784,8 +662,7 @@
 
     /**
      * Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling
-     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
-     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
+     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
      */
     public abstract static class SynchronousScreenCaptureListener extends ScreenCaptureListener {
         SynchronousScreenCaptureListener(ObjIntConsumer<ScreenshotHardwareBuffer> consumer) {
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index f40874b..7585826 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -237,13 +237,14 @@
                         PixelFormat.RGBA_8888,
                         GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
                                 | GraphicBuffer.USAGE_SW_WRITE_RARELY);
-                if (background == null) {
+                final Canvas c = background != null ? background.lockCanvas() : null;
+                if (c == null) {
                     Log.e(TAG, "Unable to draw snapshot: failed to allocate graphic buffer for "
                             + mTitle);
+                    mTransaction.clear();
+                    childSurfaceControl.release();
                     return;
                 }
-                // TODO: Support this on HardwareBuffer
-                final Canvas c = background.lockCanvas();
                 drawBackgroundAndBars(c, frame);
                 background.unlockCanvasAndPost(c);
                 mTransaction.setBuffer(mRootSurface,
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 451acbe..a88e394 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -122,7 +122,7 @@
             TYPE_PARAMETER_PROCESS_RUNNING,
             TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
             TYPE_PARAMETER_ACTIVITY_CREATED,
-            TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
+            TYPE_PARAMETER_ALLOW_ICON,
             TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
             TYPE_PARAMETER_WINDOWLESS,
             TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
@@ -143,7 +143,7 @@
     /** @hide */
     public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
     /** @hide */
-    public static final int TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN = 0x00000020;
+    public static final int TYPE_PARAMETER_ALLOW_ICON = 0x00000020;
     /**
      * The parameter which indicates if the activity has finished drawing.
      * @hide
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index edea297..9b10a7f 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -36,11 +36,17 @@
     private final @WindowManager.TransitionType int mType;
 
     /**
-     * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+     * If non-null, the task containing the activity whose lifecycle change (start or
      * finish) has caused this transition to occur.
      */
     private @Nullable ActivityManager.RunningTaskInfo mTriggerTask;
 
+    /**
+     * If non-null, the task containing the pip activity that participates in this
+     * transition.
+     */
+    private @Nullable ActivityManager.RunningTaskInfo mPipTask;
+
     /** If non-null, a remote-transition associated with the source of this transition. */
     private @Nullable RemoteTransition mRemoteTransition;
 
@@ -59,7 +65,8 @@
             @WindowManager.TransitionType int type,
             @Nullable ActivityManager.RunningTaskInfo triggerTask,
             @Nullable RemoteTransition remoteTransition) {
-        this(type, triggerTask, remoteTransition, null /* displayChange */, 0 /* flags */);
+        this(type, triggerTask, null /* pipTask */,
+                remoteTransition, null /* displayChange */, 0 /* flags */);
     }
 
     /** constructor override */
@@ -68,7 +75,17 @@
             @Nullable ActivityManager.RunningTaskInfo triggerTask,
             @Nullable RemoteTransition remoteTransition,
             int flags) {
-        this(type, triggerTask, remoteTransition, null /* displayChange */, flags);
+        this(type, triggerTask, null /* pipTask */,
+                remoteTransition, null /* displayChange */, flags);
+    }
+
+    public TransitionRequestInfo(
+            @WindowManager.TransitionType int type,
+            @Nullable ActivityManager.RunningTaskInfo triggerTask,
+            @Nullable RemoteTransition remoteTransition,
+            @Nullable TransitionRequestInfo.DisplayChange displayChange,
+            int flags) {
+        this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
     }
 
     /** Requested change to a display. */
@@ -246,7 +263,7 @@
         };
 
         @DataClass.Generated(
-                time = 1691627678294L,
+                time = 1693425051905L,
                 codegenVersion = "1.0.23",
                 sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
                 inputSignatures = "private final  int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate  int mStartRotation\nprivate  int mEndRotation\nprivate  boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -283,6 +300,9 @@
      * @param triggerTask
      *   If non-null, If non-null, the task containing the activity whose lifecycle change (start or
      *   finish) has caused this transition to occur.
+     * @param pipTask
+     *   If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+     *   finish) has caused this transition to occur.
      * @param remoteTransition
      *   If non-null, a remote-transition associated with the source of this transition.
      * @param displayChange
@@ -296,6 +316,7 @@
     public TransitionRequestInfo(
             @WindowManager.TransitionType int type,
             @Nullable ActivityManager.RunningTaskInfo triggerTask,
+            @Nullable ActivityManager.RunningTaskInfo pipTask,
             @Nullable RemoteTransition remoteTransition,
             @Nullable TransitionRequestInfo.DisplayChange displayChange,
             int flags) {
@@ -303,6 +324,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 WindowManager.TransitionType.class, null, mType);
         this.mTriggerTask = triggerTask;
+        this.mPipTask = pipTask;
         this.mRemoteTransition = remoteTransition;
         this.mDisplayChange = displayChange;
         this.mFlags = flags;
@@ -319,7 +341,7 @@
     }
 
     /**
-     * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+     * If non-null, the task containing the activity whose lifecycle change (start or
      * finish) has caused this transition to occur.
      */
     @DataClass.Generated.Member
@@ -328,6 +350,15 @@
     }
 
     /**
+     * If non-null, the task containing the pip activity that participates in this
+     * transition.
+     */
+    @DataClass.Generated.Member
+    public @Nullable ActivityManager.RunningTaskInfo getPipTask() {
+        return mPipTask;
+    }
+
+    /**
      * If non-null, a remote-transition associated with the source of this transition.
      */
     @DataClass.Generated.Member
@@ -354,7 +385,7 @@
     }
 
     /**
-     * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+     * If non-null, the task containing the activity whose lifecycle change (start or
      * finish) has caused this transition to occur.
      */
     @DataClass.Generated.Member
@@ -364,6 +395,16 @@
     }
 
     /**
+     * If non-null, the task containing the pip activity that participates in this
+     * transition.
+     */
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull TransitionRequestInfo setPipTask(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
+        mPipTask = value;
+        return this;
+    }
+
+    /**
      * If non-null, a remote-transition associated with the source of this transition.
      */
     @DataClass.Generated.Member
@@ -392,6 +433,7 @@
         return "TransitionRequestInfo { " +
                 "type = " + mType + ", " +
                 "triggerTask = " + mTriggerTask + ", " +
+                "pipTask = " + mPipTask + ", " +
                 "remoteTransition = " + mRemoteTransition + ", " +
                 "displayChange = " + mDisplayChange + ", " +
                 "flags = " + mFlags +
@@ -406,11 +448,13 @@
 
         byte flg = 0;
         if (mTriggerTask != null) flg |= 0x2;
-        if (mRemoteTransition != null) flg |= 0x4;
-        if (mDisplayChange != null) flg |= 0x8;
+        if (mPipTask != null) flg |= 0x4;
+        if (mRemoteTransition != null) flg |= 0x8;
+        if (mDisplayChange != null) flg |= 0x10;
         dest.writeByte(flg);
         dest.writeInt(mType);
         if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
+        if (mPipTask != null) dest.writeTypedObject(mPipTask, flags);
         if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
         if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
         dest.writeInt(mFlags);
@@ -430,14 +474,16 @@
         byte flg = in.readByte();
         int type = in.readInt();
         ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
-        RemoteTransition remoteTransition = (flg & 0x4) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
-        TransitionRequestInfo.DisplayChange displayChange = (flg & 0x8) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
+        ActivityManager.RunningTaskInfo pipTask = (flg & 0x4) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+        RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
+        TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
         int flags = in.readInt();
 
         this.mType = type;
         com.android.internal.util.AnnotationValidations.validate(
                 WindowManager.TransitionType.class, null, mType);
         this.mTriggerTask = triggerTask;
+        this.mPipTask = pipTask;
         this.mRemoteTransition = remoteTransition;
         this.mDisplayChange = displayChange;
         this.mFlags = flags;
@@ -460,10 +506,10 @@
     };
 
     @DataClass.Generated(
-            time = 1691627678327L,
+            time = 1693425051928L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
-            inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final  int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+            inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final  int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
deleted file mode 100644
index f1d981a..0000000
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "com.android.window.flags"
-
-flag {
-  name: "letterbox_background_wallpaper_flag"
-  namespace: "large_screen_experiences_app_compat"
-  description: "Whether the letterbox wallpaper style is enabled by default"
-  bug: "297195682"
-}
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index b9f0236..d2fdc65 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -61,6 +61,8 @@
     private final ModelBuilder mModelBuilder;
     private ResolverComparatorModel mComparatorModel;
 
+    private ResolverAppPredictorCallback mSortingCallback;
+
     // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
     // back to using the ResolverRankerService.
     // TODO: responsibility for this fallback behavior can live outside of the AppPrediction client.
@@ -94,6 +96,9 @@
             // TODO: may not be necessary to build a new model, since we're destroying anyways.
             mComparatorModel = mModelBuilder.buildFallbackModel(mResolverRankerService);
         }
+        if (mSortingCallback != null) {
+            mSortingCallback.destroy();
+        }
     }
 
     @Override
@@ -140,22 +145,27 @@
                     .setClassName(target.name.getClassName())
                     .build());
         }
+
+        if (mSortingCallback != null) {
+            mSortingCallback.destroy();
+        }
+        mSortingCallback = new ResolverAppPredictorCallback(sortedAppTargets -> {
+            if (sortedAppTargets.isEmpty()) {
+                Log.i(TAG, "AppPredictionService disabled. Using resolver.");
+                setupFallbackModel(targets);
+            } else {
+                Log.i(TAG, "AppPredictionService response received");
+                // Skip sending to Handler which takes extra time to dispatch messages.
+                // TODO: the Handler guards some concurrency conditions, so this could
+                // probably result in a race (we're not currently on the Handler thread?).
+                // We'll leave this as-is since we intend to remove the Handler design
+                // shortly, but this is still an unsound shortcut.
+                handleResult(sortedAppTargets);
+            }
+        });
+
         mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(),
-                sortedAppTargets -> {
-                    if (sortedAppTargets.isEmpty()) {
-                        Log.i(TAG, "AppPredictionService disabled. Using resolver.");
-                        setupFallbackModel(targets);
-                    } else {
-                        Log.i(TAG, "AppPredictionService response received");
-                        // Skip sending to Handler which takes extra time to dispatch messages.
-                        // TODO: the Handler guards some concurrency conditions, so this could
-                        // probably result in a race (we're not currently on the Handler thread?).
-                        // We'll leave this as-is since we intend to remove the Handler design
-                        // shortly, but this is still an unsound shortcut.
-                        handleResult(sortedAppTargets);
-                    }
-                }
-        );
+                mSortingCallback.asConsumer());
     }
 
     private void setupFallbackModel(List<ResolvedComponentInfo> targets) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2b39bb4..7e2c017 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -24,9 +24,7 @@
 import static android.content.ContentProvider.getUserIdFromUri;
 import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
 import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
-
 import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.animation.Animator;
@@ -777,9 +775,9 @@
         return appPredictor;
     }
 
-    private AppPredictor.Callback createAppPredictorCallback(
+    private ResolverAppPredictorCallback createAppPredictorCallback(
             ChooserListAdapter chooserListAdapter) {
-        return resultList -> {
+        return new ResolverAppPredictorCallback(resultList -> {
             if (isFinishing() || isDestroyed()) {
                 return;
             }
@@ -811,7 +809,7 @@
             }
             sendShareShortcutInfoList(shareShortcutInfos, chooserListAdapter, resultList,
                     chooserListAdapter.getUserHandle());
-        };
+        });
     }
 
     static SharedPreferences getPinnedSharedPrefs(Context context) {
@@ -2559,10 +2557,13 @@
             boolean filterLastUsed, UserHandle userHandle) {
         ChooserListAdapter chooserListAdapter = createChooserListAdapter(context, payloadIntents,
                 initialIntents, rList, filterLastUsed, userHandle);
-        AppPredictor.Callback appPredictorCallback = createAppPredictorCallback(chooserListAdapter);
+        ResolverAppPredictorCallback appPredictorCallbackWrapper =
+                createAppPredictorCallback(chooserListAdapter);
+        AppPredictor.Callback appPredictorCallback = appPredictorCallbackWrapper.asCallback();
         AppPredictor appPredictor = setupAppPredictorForUser(userHandle, appPredictorCallback);
         chooserListAdapter.setAppPredictor(appPredictor);
-        chooserListAdapter.setAppPredictorCallback(appPredictorCallback);
+        chooserListAdapter.setAppPredictorCallback(
+                appPredictorCallback, appPredictorCallbackWrapper);
         return new ChooserGridAdapter(chooserListAdapter);
     }
 
@@ -3023,28 +3024,31 @@
         return shouldShowTabs()
                 && (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
                         UserHandle.of(UserHandle.myUserId())).getCount() > 0
-                    || shouldShowContentPreviewWhenEmpty())
+                    || shouldShowStickyContentPreviewWhenEmpty())
                 && shouldShowContentPreview();
     }
 
     /**
-     * This method could be used to override the default behavior when we hide the preview area
-     * when the current tab doesn't have any items.
+     * This method could be used to override the default behavior when we hide the sticky preview
+     * area when the current tab doesn't have any items.
      *
-     * @return true if we want to show the content preview area even if the tab for the current
-     *         user is empty
+     * @return {@code true} if we want to show the sticky content preview area even if the tab for
+     *         the current user is empty
      */
-    protected boolean shouldShowContentPreviewWhenEmpty() {
+    protected boolean shouldShowStickyContentPreviewWhenEmpty() {
         return false;
     }
 
-    /**
-     * @return true if we want to show the content preview area
-     */
-    protected boolean shouldShowContentPreview() {
+    @Override
+    public boolean shouldShowContentPreview() {
         return isSendAction(getTargetIntent());
     }
 
+    @Override
+    public boolean shouldShowServiceTargets() {
+        return shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic();
+    }
+
     private void updateStickyContentPreview() {
         if (shouldShowStickyContentPreviewNoOrientationCheck()) {
             // The sticky content preview is only shown when we show the work and personal tabs.
@@ -3406,11 +3410,7 @@
         // There can be at most one row in the listview, that is internally
         // a ViewGroup with 2 rows
         public int getServiceTargetRowCount() {
-            if (shouldShowContentPreview()
-                    && !ActivityManager.isLowRamDeviceStatic()) {
-                return 1;
-            }
-            return 0;
+            return shouldShowServiceTargets() ? 1 : 0;
         }
 
         public int getAzLabelRowCount() {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 1eecb41..b3e828d 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -19,7 +19,6 @@
 import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
 import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
 
-import android.app.ActivityManager;
 import android.app.prediction.AppPredictor;
 import android.content.ComponentName;
 import android.content.Context;
@@ -103,6 +102,7 @@
     // Sorted list of DisplayResolveInfos for the alphabetical app section.
     private List<DisplayResolveInfo> mSortedList = new ArrayList<>();
     private AppPredictor mAppPredictor;
+    private ResolverAppPredictorCallback mAppPredictorCallbackWrapper;
     private AppPredictor.Callback mAppPredictorCallback;
 
     // Represents the UserSpace in which the Initial Intents should be resolved.
@@ -425,11 +425,9 @@
     }
 
     public int getServiceTargetCount() {
-        if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent())
-                && !ActivityManager.isLowRamDeviceStatic()) {
+        if (mChooserListCommunicator.shouldShowServiceTargets()) {
             return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets());
         }
-
         return 0;
     }
 
@@ -747,8 +745,11 @@
         mAppPredictor = appPredictor;
     }
 
-    public void setAppPredictorCallback(AppPredictor.Callback appPredictorCallback) {
+    public void setAppPredictorCallback(
+            AppPredictor.Callback appPredictorCallback,
+            ResolverAppPredictorCallback appPredictorCallbackWrapper) {
         mAppPredictorCallback = appPredictorCallback;
+        mAppPredictorCallbackWrapper = appPredictorCallbackWrapper;
     }
 
     public void destroyAppPredictor() {
@@ -757,6 +758,10 @@
             getAppPredictor().destroy();
             setAppPredictor(null);
         }
+
+        if (mAppPredictorCallbackWrapper != null) {
+            mAppPredictorCallbackWrapper.destroy();
+        }
     }
 
     /**
@@ -771,6 +776,10 @@
         void sendListViewUpdateMessage(UserHandle userHandle);
 
         boolean isSendAction(Intent targetIntent);
+
+        boolean shouldShowContentPreview();
+
+        boolean shouldShowServiceTargets();
     }
 
     /**
diff --git a/core/java/com/android/internal/app/ResolverAppPredictorCallback.java b/core/java/com/android/internal/app/ResolverAppPredictorCallback.java
new file mode 100644
index 0000000..c35e536
--- /dev/null
+++ b/core/java/com/android/internal/app/ResolverAppPredictorCallback.java
@@ -0,0 +1,55 @@
+/*
+ * 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.app;
+
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Callback wrapper that works around potential memory leaks in app predictor.
+ *
+ * Nulls the callback itself when destroyed, so at worst you'll leak just this object.
+ */
+public class ResolverAppPredictorCallback {
+    private volatile Consumer<List<AppTarget>> mCallback;
+
+    public ResolverAppPredictorCallback(Consumer<List<AppTarget>> callback) {
+        mCallback = callback;
+    }
+
+    private void notifyCallback(List<AppTarget> list) {
+        Consumer<List<AppTarget>> callback = mCallback;
+        if (callback != null) {
+            callback.accept(Objects.requireNonNullElseGet(list, List::of));
+        }
+    }
+
+    public Consumer<List<AppTarget>> asConsumer() {
+        return this::notifyCallback;
+    }
+
+    public AppPredictor.Callback asCallback() {
+        return this::notifyCallback;
+    }
+
+    public void destroy() {
+        mCallback = null;
+    }
+}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 8de448b..4a848f6 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -80,6 +80,10 @@
 
         public static final Flag PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS = releasedFlag(
                 "persist.sysui.notification.propagate_channel_updates_to_conversations");
+
+        /** b/301242692: Visit extra URIs used in notifications to prevent security issues. */
+        public static final Flag VISIT_RISKY_URIS = devFlag(
+                "persist.sysui.notification.visit_risky_uris");
     }
 
     //// == End of flags.  Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9b5a3f7..e014ab0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -40,6 +40,9 @@
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.app.SearchManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -123,6 +126,7 @@
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
 import com.android.internal.widget.DecorContentParent;
+import com.android.window.flags.Flags;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -162,6 +166,14 @@
 
     private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
 
+    /**
+     * Makes navigation bar color transparent by default if the target SDK is
+     * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static final long NAV_BAR_COLOR_DEFAULT_TRANSPARENT = 232195501L;
+
     private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
             (1 << FEATURE_CUSTOM_TITLE) |
             (1 << FEATURE_CONTENT_TRANSITIONS) |
@@ -2525,6 +2537,8 @@
 
             mNavigationBarColor =
                     navBarColor == navBarDefaultColor
+                            && !(CompatChanges.isChangeEnabled(NAV_BAR_COLOR_DEFAULT_TRANSPARENT)
+                                    && Flags.navBarTransparentByDefault())
                             && !context.getResources().getBoolean(
                                     R.bool.config_navBarDefaultTransparent)
                     ? navBarCompatibleColor
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 483a184..8f4df80 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -1304,8 +1304,9 @@
         t.setBuffer(layer, buffer.getHardwareBuffer());
         t.setDataSpace(layer, buffer.getColorSpace().getDataSpace());
         // Avoid showing dimming effect for HDR content when running animations.
-        // TODO(b/298219334): Only do this if we know we already dimmed in the screenshot
-        t.setDimmingEnabled(layer, false);
+        if (buffer.containsHdrLayers()) {
+            t.setDimmingEnabled(layer, false);
+        }
     }
 
     /** Returns whether the hardware buffer passed in is marked as protected. */
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 52ffc98..a513ca5 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IdRes;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -53,10 +54,13 @@
     private static final String TAG = "ResolverDrawerLayout";
     private MetricsLogger mMetricsLogger;
 
+
+
     /**
-     * Max width of the whole drawer layout
+     * Max width of the whole drawer layout and its res id
      */
-    private final int mMaxWidth;
+    private int mMaxWidthResId;
+    private int mMaxWidth;
 
     /**
      * Max total visible height of views not marked always-show when in the closed/initial state
@@ -152,6 +156,7 @@
 
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ResolverDrawerLayout,
                 defStyleAttr, 0);
+        mMaxWidthResId = a.getResourceId(R.styleable.ResolverDrawerLayout_maxWidth, -1);
         mMaxWidth = a.getDimensionPixelSize(R.styleable.ResolverDrawerLayout_maxWidth, -1);
         mMaxCollapsedHeight = a.getDimensionPixelSize(
                 R.styleable.ResolverDrawerLayout_maxCollapsedHeight, 0);
@@ -1042,6 +1047,18 @@
         return mAlwaysShowHeight;
     }
 
+    /**
+     * Max width of the drawer needs to be updated after the configuration is changed.
+     * For example, foldables have different layout width when the device is folded and unfolded.
+     */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mMaxWidthResId > 0) {
+            mMaxWidth = getResources().getDimensionPixelSize(mMaxWidthResId);
+        }
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int width = getWidth();
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fe95762..e0bcef6 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -565,6 +565,51 @@
     return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
 }
 
+static jlong android_os_Debug_getRssPid(JNIEnv* env, jobject clazz, jint pid,
+                                        jlongArray outMemtrack) {
+    jlong rss = 0;
+    jlong memtrack = 0;
+
+    struct graphics_memory_pss graphics_mem;
+    if (read_memtrack_memory(pid, &graphics_mem) == 0) {
+        rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
+    }
+
+    ::android::meminfo::ProcMemInfo proc_mem(pid);
+    uint64_t status_rss;
+    if (proc_mem.StatusVmRSS(&status_rss)) {
+        rss += status_rss;
+    } else {
+        return 0;
+    }
+
+    if (outMemtrack != NULL) {
+        int outLen = env->GetArrayLength(outMemtrack);
+        if (outLen >= 1) {
+            jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
+            if (outMemtrackArray != NULL) {
+                outMemtrackArray[0] = memtrack;
+                if (outLen >= 2) {
+                    outMemtrackArray[1] = graphics_mem.graphics;
+                }
+                if (outLen >= 3) {
+                    outMemtrackArray[2] = graphics_mem.gl;
+                }
+                if (outLen >= 4) {
+                    outMemtrackArray[3] = graphics_mem.other;
+                }
+            }
+            env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
+        }
+    }
+
+    return rss;
+}
+
+static jlong android_os_Debug_getRss(JNIEnv* env, jobject clazz) {
+    return android_os_Debug_getRssPid(env, clazz, getpid(), NULL);
+}
+
 // The 1:1 mapping of MEMINFO_* enums here must match with the constants from
 // Debug.java.
 enum {
@@ -974,62 +1019,43 @@
  */
 
 static const JNINativeMethod gMethods[] = {
-    { "getNativeHeapSize",      "()J",
-            (void*) android_os_Debug_getNativeHeapSize },
-    { "getNativeHeapAllocatedSize", "()J",
-            (void*) android_os_Debug_getNativeHeapAllocatedSize },
-    { "getNativeHeapFreeSize",  "()J",
-            (void*) android_os_Debug_getNativeHeapFreeSize },
-    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
-            (void*) android_os_Debug_getDirtyPages },
-    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)Z",
-            (void*) android_os_Debug_getDirtyPagesPid },
-    { "getPss",                 "()J",
-            (void*) android_os_Debug_getPss },
-    { "getPss",                 "(I[J[J)J",
-            (void*) android_os_Debug_getPssPid },
-    { "getMemInfo",             "([J)V",
-            (void*) android_os_Debug_getMemInfo },
-    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
-            (void*) android_os_Debug_dumpNativeHeap },
-    { "dumpNativeMallocInfo",   "(Ljava/io/FileDescriptor;)V",
-            (void*) android_os_Debug_dumpNativeMallocInfo },
-    { "getBinderSentTransactions", "()I",
-            (void*) android_os_Debug_getBinderSentTransactions },
-    { "getBinderReceivedTransactions", "()I",
-            (void*) android_os_getBinderReceivedTransactions },
-    { "getBinderLocalObjectCount", "()I",
-            (void*)android_os_Debug_getLocalObjectCount },
-    { "getBinderProxyObjectCount", "()I",
-            (void*)android_os_Debug_getProxyObjectCount },
-    { "getBinderDeathObjectCount", "()I",
-            (void*)android_os_Debug_getDeathObjectCount },
-    { "dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
-            (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout },
-    { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
-            (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
-    { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
-            (void*)android_os_Debug_getUnreachableMemory },
-    { "getZramFreeKb", "()J",
-            (void*)android_os_Debug_getFreeZramKb },
-    { "getIonHeapsSizeKb", "()J",
-            (void*)android_os_Debug_getIonHeapsSizeKb },
-    { "getDmabufTotalExportedKb", "()J",
-            (void*)android_os_Debug_getDmabufTotalExportedKb },
-    { "getGpuPrivateMemoryKb", "()J",
-            (void*)android_os_Debug_getGpuPrivateMemoryKb },
-    { "getDmabufHeapTotalExportedKb", "()J",
-            (void*)android_os_Debug_getDmabufHeapTotalExportedKb },
-    { "getIonPoolsSizeKb", "()J",
-            (void*)android_os_Debug_getIonPoolsSizeKb },
-    { "getDmabufMappedSizeKb", "()J",
-            (void*)android_os_Debug_getDmabufMappedSizeKb },
-    { "getDmabufHeapPoolsSizeKb", "()J",
-            (void*)android_os_Debug_getDmabufHeapPoolsSizeKb },
-    { "getGpuTotalUsageKb", "()J",
-            (void*)android_os_Debug_getGpuTotalUsageKb },
-    { "isVmapStack", "()Z",
-            (void*)android_os_Debug_isVmapStack },
+        {"getNativeHeapSize", "()J", (void*)android_os_Debug_getNativeHeapSize},
+        {"getNativeHeapAllocatedSize", "()J", (void*)android_os_Debug_getNativeHeapAllocatedSize},
+        {"getNativeHeapFreeSize", "()J", (void*)android_os_Debug_getNativeHeapFreeSize},
+        {"getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
+         (void*)android_os_Debug_getDirtyPages},
+        {"getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
+         (void*)android_os_Debug_getDirtyPagesPid},
+        {"getPss", "()J", (void*)android_os_Debug_getPss},
+        {"getPss", "(I[J[J)J", (void*)android_os_Debug_getPssPid},
+        {"getRss", "()J", (void*)android_os_Debug_getRss},
+        {"getRss", "(I[J)J", (void*)android_os_Debug_getRssPid},
+        {"getMemInfo", "([J)V", (void*)android_os_Debug_getMemInfo},
+        {"dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Debug_dumpNativeHeap},
+        {"dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V",
+         (void*)android_os_Debug_dumpNativeMallocInfo},
+        {"getBinderSentTransactions", "()I", (void*)android_os_Debug_getBinderSentTransactions},
+        {"getBinderReceivedTransactions", "()I", (void*)android_os_getBinderReceivedTransactions},
+        {"getBinderLocalObjectCount", "()I", (void*)android_os_Debug_getLocalObjectCount},
+        {"getBinderProxyObjectCount", "()I", (void*)android_os_Debug_getProxyObjectCount},
+        {"getBinderDeathObjectCount", "()I", (void*)android_os_Debug_getDeathObjectCount},
+        {"dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
+         (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout},
+        {"dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
+         (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout},
+        {"getUnreachableMemory", "(IZ)Ljava/lang/String;",
+         (void*)android_os_Debug_getUnreachableMemory},
+        {"getZramFreeKb", "()J", (void*)android_os_Debug_getFreeZramKb},
+        {"getIonHeapsSizeKb", "()J", (void*)android_os_Debug_getIonHeapsSizeKb},
+        {"getDmabufTotalExportedKb", "()J", (void*)android_os_Debug_getDmabufTotalExportedKb},
+        {"getGpuPrivateMemoryKb", "()J", (void*)android_os_Debug_getGpuPrivateMemoryKb},
+        {"getDmabufHeapTotalExportedKb", "()J",
+         (void*)android_os_Debug_getDmabufHeapTotalExportedKb},
+        {"getIonPoolsSizeKb", "()J", (void*)android_os_Debug_getIonPoolsSizeKb},
+        {"getDmabufMappedSizeKb", "()J", (void*)android_os_Debug_getDmabufMappedSizeKb},
+        {"getDmabufHeapPoolsSizeKb", "()J", (void*)android_os_Debug_getDmabufHeapPoolsSizeKb},
+        {"getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb},
+        {"isVmapStack", "()Z", (void*)android_os_Debug_isVmapStack},
 };
 
 int register_android_os_Debug(JNIEnv *env)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 6ed0a8a..041f9c7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -158,12 +158,8 @@
 // ****************************************************************************
 // ****************************************************************************
 
-static constexpr int32_t PROXY_WARN_INTERVAL = 5000;
 static constexpr uint32_t GC_INTERVAL = 1000;
 
-static std::atomic<uint32_t> gNumProxies(0);
-static std::atomic<uint32_t> gProxiesWarned(0);
-
 // Number of GlobalRefs held by JavaBBinders.
 static std::atomic<uint32_t> gNumLocalRefsCreated(0);
 static std::atomic<uint32_t> gNumLocalRefsDeleted(0);
@@ -776,19 +772,7 @@
         return NULL;
     }
     BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
-    if (actualNativeData == nativeData) {
-        // Created a new Proxy
-        uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
-        uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
-        if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
-            // Multiple threads can get here, make sure only one of them gets to
-            // update the warn counter.
-            if (gProxiesWarned.compare_exchange_strong(numLastWarned,
-                        numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
-                ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
-            }
-        }
-    } else {
+    if (actualNativeData != nativeData) {
         delete nativeData;
     }
 
@@ -1143,7 +1127,7 @@
 
 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
 {
-    return gNumProxies.load();
+    return BpBinder::getBinderProxyCount();
 }
 
 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
@@ -1428,7 +1412,6 @@
             nativeData->mObject.get(), nativeData->mOrgue.get());
     delete nativeData;
     IPCThreadState::self()->flushCommands();
-    --gNumProxies;
 }
 
 JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclass) {
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ddaeb5a..7f69e22 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -240,6 +240,36 @@
     return static_cast<jboolean>(*map1 == *map2);
 }
 
+static void nativeApplyOverlay(JNIEnv* env, jobject clazz, jlong ptr, jstring nameObj,
+        jstring overlayObj) {
+    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+    if (!map || !map->getMap()) {
+        return;
+    }
+    ScopedUtfChars nameChars(env, nameObj);
+    ScopedUtfChars overlayChars(env, overlayObj);
+    base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+            KeyCharacterMap::loadContents(nameChars.c_str(), overlayChars.c_str(),
+                                          KeyCharacterMap::Format::OVERLAY);
+    if (ret.ok()) {
+        std::shared_ptr<KeyCharacterMap> overlay = *ret;
+        map->getMap()->combine(*overlay);
+    }
+}
+
+static jint nativeGetMappedKey(JNIEnv* env, jobject clazz, jlong ptr, jint scanCode) {
+    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+    if (!map || !map->getMap()) {
+        return 0;
+    }
+    int32_t outKeyCode;
+    status_t mapKeyRes = map->getMap()->mapKey(scanCode, /*usageCode=*/0, &outKeyCode);
+    if (mapKeyRes != OK) {
+        return 0;
+    }
+    return static_cast<jint>(outKeyCode);
+}
+
 /*
  * JNI registration.
  */
@@ -260,7 +290,9 @@
         {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
          (void*)nativeObtainEmptyKeyCharacterMap},
         {"nativeEquals", "(JJ)Z", (void*)nativeEquals},
-};
+        {"nativeApplyOverlay", "(JLjava/lang/String;Ljava/lang/String;)V",
+         (void*)nativeApplyOverlay},
+        {"nativeGetMappedKey", "(JI)I", (void*)nativeGetMappedKey}};
 
 int register_android_view_KeyCharacterMap(JNIEnv* env)
 {
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index bdf7eaa..beb8c9b 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -50,13 +50,6 @@
 } gCaptureArgsClassInfo;
 
 static struct {
-    jfieldID displayToken;
-    jfieldID width;
-    jfieldID height;
-    jfieldID useIdentityTransform;
-} gDisplayCaptureArgsClassInfo;
-
-static struct {
     jfieldID layer;
     jfieldID childrenOnly;
 } gLayerCaptureArgsClassInfo;
@@ -181,39 +174,6 @@
                                  gCaptureArgsClassInfo.hintForSeamlessTransition);
 }
 
-static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
-                                                       jobject displayCaptureArgsObject) {
-    DisplayCaptureArgs captureArgs;
-    getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
-
-    captureArgs.displayToken =
-            ibinderForJavaObject(env,
-                                 env->GetObjectField(displayCaptureArgsObject,
-                                                     gDisplayCaptureArgsClassInfo.displayToken));
-    captureArgs.width =
-            env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
-    captureArgs.height =
-            env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
-    captureArgs.useIdentityTransform =
-            env->GetBooleanField(displayCaptureArgsObject,
-                                 gDisplayCaptureArgsClassInfo.useIdentityTransform);
-    return captureArgs;
-}
-
-static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
-                                 jlong screenCaptureListenerObject) {
-    const DisplayCaptureArgs captureArgs =
-            displayCaptureArgsFromObject(env, displayCaptureArgsObject);
-
-    if (captureArgs.displayToken == nullptr) {
-        return BAD_VALUE;
-    }
-
-    sp<gui::IScreenCaptureListener> captureListener =
-            reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
-    return ScreenshotClient::captureDisplay(captureArgs, captureListener);
-}
-
 static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
                                 jlong screenCaptureListenerObject) {
     LayerCaptureArgs captureArgs;
@@ -283,8 +243,6 @@
 
 static const JNINativeMethod sScreenCaptureMethods[] = {
         // clang-format off
-    {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
-            (void*)nativeCaptureDisplay },
     {"nativeCaptureLayers",  "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
             (void*)nativeCaptureLayers },
     {"nativeCreateScreenCaptureListener", "(Ljava/util/function/ObjIntConsumer;)J",
@@ -317,17 +275,6 @@
     gCaptureArgsClassInfo.hintForSeamlessTransition =
             GetFieldIDOrDie(env, captureArgsClazz, "mHintForSeamlessTransition", "Z");
 
-    jclass displayCaptureArgsClazz =
-            FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs");
-    gDisplayCaptureArgsClassInfo.displayToken =
-            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mDisplayToken", "Landroid/os/IBinder;");
-    gDisplayCaptureArgsClassInfo.width =
-            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mWidth", "I");
-    gDisplayCaptureArgsClassInfo.height =
-            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
-    gDisplayCaptureArgsClassInfo.useIdentityTransform =
-            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
-
     jclass layerCaptureArgsClazz =
             FindClassOrDie(env, "android/window/ScreenCapture$LayerCaptureArgs");
     gLayerCaptureArgsClassInfo.layer =
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 279a5d0..b9905e8 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -30,6 +30,7 @@
     optional string non_localized_label = 4;
     optional int32 icon = 5;
     optional int32 banner = 6;
+    optional bool is_archived = 7;
 }
 
 // Proto of android.content.pm.ApplicationInfo which extends PackageItemInfo
diff --git a/core/proto/android/inputmethodservice/softinputwindow.proto b/core/proto/android/inputmethodservice/softinputwindow.proto
index e0ba6bf..32d14f0 100644
--- a/core/proto/android/inputmethodservice/softinputwindow.proto
+++ b/core/proto/android/inputmethodservice/softinputwindow.proto
@@ -27,6 +27,6 @@
     reserved 2;  // window_type
     reserved 3;  // gravity
     reserved 4;  // takes_focus
-    optional .android.graphics.RectProto bounds = 5;
+    reserved 5;  // bounds
     optional int32 window_state = 6;
 }
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3180ffb..c09f0a3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1699,6 +1699,15 @@
         android:description="@string/permdesc_cameraOpenCloseListener"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows camera access by Headless System User 0 when device is running in
+            HSUM Mode.
+    @hide -->
+    <permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"
+        android:permissionGroup="android.permission-group.UNDEFINED"
+        android:label="@string/permlab_cameraHeadlessSystemUser"
+        android:description="@string/permdesc_cameraHeadlessSystemUser"
+        android:protectionLevel="signature" />
+
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device sensors                           -->
     <!-- ====================================================================== -->
@@ -7654,6 +7663,10 @@
     <permission android:name="android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA"
                 android:protectionLevel="signature" />
 
+    <!-- @hide @TestApi Allows tests running in CTS-in-sandbox mode to launch activities -->
+    <permission android:name="android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows the holder to call health connect migration APIs.
         @hide -->
     <permission android:name="android.permission.MIGRATE_HEALTH_CONNECT_DATA"
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 5319762..4027f5c 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -81,4 +81,9 @@
     <!-- Floating windows can be fullscreen (i.e. windowIsFloating can still have fullscreen
          window that does not wrap content). -->
     <bool name="config_allowFloatingWindowsFillScreen">true</bool>
+
+    <!-- Whether scroll haptic feedback is enabled for rotary encoder scrolls on
+         {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER}
+         devices. -->
+    <bool name="config_viewRotaryEncoderHapticScrollFedbackEnabled">true</bool>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d09bf44..2e2ec5b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2814,7 +2814,7 @@
 
     <!-- Base "handwriting slop" value used by ViewConfiguration as a
      movement threshold where stylus handwriting should begin. -->
-    <dimen name="config_viewConfigurationHandwritingSlop">4dp</dimen>
+    <dimen name="config_viewConfigurationHandwritingSlop">2dp</dimen>
 
     <!-- Base "hover slop" value used by ViewConfiguration as a
          movement threshold under which hover is considered "stationary". -->
@@ -5451,6 +5451,10 @@
          to enroll the other eligible biometric. -->
     <fraction name="config_biometricNotificationFrrThreshold">25%</fraction>
 
+    <!-- Whether to enable the biometric notification for dual-modality device that enrolled a
+         single biometric and experiences high FRR. -->
+    <bool name="config_biometricFrrNotificationEnabled">false</bool>
+
     <!-- The component name for the default profile supervisor, which can be set as a profile owner
     even after user setup is complete. The defined component should be used for supervision purposes
     only. The component must be part of a system app. -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 6bb87f3..878e6b3 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -151,6 +151,27 @@
     <integer name="config_timeout_to_receive_delivered_ack_millis">300000</integer>
     <java-symbol type="integer" name="config_timeout_to_receive_delivered_ack_millis" />
 
+    <!-- The time duration in millis that the satellite will stay at listening state to wait for the
+         next incoming page before disabling listening and moving to IDLE state. This timeout
+         duration is used when transitioning from sending state to listening state.
+         -->
+    <integer name="config_satellite_stay_at_listening_from_sending_millis">180000</integer>
+    <java-symbol type="integer" name="config_satellite_stay_at_listening_from_sending_millis" />
+
+    <!-- The time duration in millis that the satellite will stay at listening state to wait for the
+         next incoming page before disabling listening and moving to IDLE state. This timeout
+         duration is used when transitioning from receiving state to listening state.
+         -->
+    <integer name="config_satellite_stay_at_listening_from_receiving_millis">30000</integer>
+    <java-symbol type="integer" name="config_satellite_stay_at_listening_from_receiving_millis" />
+
+    <!-- The time duration in millis after which cellular scanning will be enabled and satellite
+         will move to IDLE state. This timeout duration is used for satellite with NB IOT radio
+         technologies.
+         -->
+    <integer name="config_satellite_nb_iot_inactivity_timeout_millis">180000</integer>
+    <java-symbol type="integer" name="config_satellite_nb_iot_inactivity_timeout_millis" />
+
     <!-- Telephony config for services supported by satellite providers. The format of each config
          string in the array is as follows: "PLMN_1:service_1,service_2,..."
          where PLMN is the satellite PLMN of a provider and service is an integer with the
@@ -169,6 +190,11 @@
     </string-array>
     <java-symbol type="array" name="config_satellite_services_supported_by_providers" />
 
+    <!-- The identifier of the satellite's eSIM profile preloaded on the device. The identifier is
+    composed of MCC and MNC of the satellite PLMN with the format "mccmnc". -->
+    <string name="config_satellite_esim_identifier" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_esim_identifier" />
+
     <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
          will not perform handover if the target transport is out of service, or VoPS not
          supported. The network will be torn down on the source transport, and will be
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 591e505..fac6aac 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1444,6 +1444,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
     <string name="permdesc_cameraOpenCloseListener">This app can receive callbacks when any camera device is being opened (by what application) or closed.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_cameraHeadlessSystemUser">Allow an application or service to access camera as Headless System User.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permdesc_cameraHeadlessSystemUser">This app can access camera as Headless System User.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7f1a6f9..fe11449 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2604,6 +2604,7 @@
 
   <!-- Biometric FRR config -->
   <java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
+  <java-symbol type="bool" name="config_biometricFrrNotificationEnabled" />
 
   <!-- Biometric FRR notification messages -->
   <java-symbol type="string" name="device_unlock_notification_name" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 3b099e8..af8c69e 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -216,6 +216,9 @@
     <!-- Pakistan -->
     <shortcode country="pk" pattern="\\d{1,5}" free="2057" />
 
+    <!-- Palestine: 5 digits, known premium codes listed -->
+    <shortcode country="ps" pattern="\\d{1,5}" free="37477" />
+
     <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 89b91cf..14f268a 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -89,9 +89,6 @@
     private static final ProgramSelector.Identifier TEST_DAB_FREQUENCY_ID =
             new ProgramSelector.Identifier(
                     ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, TEST_DAB_FREQUENCY_VALUE);
-    private static final ProgramSelector.Identifier TEST_FM_FREQUENCY_ID =
-            new ProgramSelector.Identifier(
-                    ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, TEST_FM_FREQUENCY_VALUE);
     private static final ProgramSelector.Identifier TEST_VENDOR_ID =
             new ProgramSelector.Identifier(
                     ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, TEST_VENDOR_ID_VALUE);
@@ -251,6 +248,20 @@
     }
 
     @Test
+    public void identifierToHalProgramIdentifier_withDeprecateDabId() {
+        long value = 0x98765ABCDL;
+        ProgramSelector.Identifier dabId = new ProgramSelector.Identifier(
+                        ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, value);
+        ProgramIdentifier halDabIdExpected = AidlTestUtils.makeHalIdentifier(
+                IdentifierType.DAB_SID_EXT, 0x987650000ABCDL);
+
+        ProgramIdentifier halDabId = ConversionUtils.identifierToHalProgramIdentifier(dabId);
+
+        expect.withMessage("Converted 28-bit DAB identifier for HAL").that(halDabId)
+                .isEqualTo(halDabIdExpected);
+    }
+
+    @Test
     public void identifierFromHalProgramIdentifier_withDabId() {
         ProgramSelector.Identifier dabId =
                 ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_DAB_SID_EXT_ID);
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 7f56eb7..9cde296 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -61,11 +61,11 @@
         "testng",
         "servicestests-utils",
         "device-time-shell-utils",
+        "testables",
     ],
 
     libs: [
         "android.test.runner",
-        "testables",
         "org.apache.http.legacy",
         "android.test.base",
         "android.test.mock",
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8935507..36e1223 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -33,7 +33,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.ActivityThread.ActivityClientRecord;
@@ -226,7 +226,7 @@
         CompatibilityInfo.setOverrideInvertedScale(scale);
         try {
             // Send process level config change.
-            ClientTransaction transaction = newTransaction(activityThread, null);
+            ClientTransaction transaction = newTransaction(activityThread);
             transaction.addCallback(ConfigurationChangeItem.obtain(
                     new Configuration(newConfig), DEVICE_ID_INVALID));
             appThread.scheduleTransaction(transaction);
@@ -243,7 +243,7 @@
             // Send activity level config change.
             newConfig.seq++;
             newConfig.smallestScreenWidthDp++;
-            transaction = newTransaction(activityThread, activity.getActivityToken());
+            transaction = newTransaction(activityThread);
             transaction.addCallback(ActivityConfigurationChangeItem.obtain(
                     activity.getActivityToken(), new Configuration(newConfig)));
             appThread.scheduleTransaction(transaction);
@@ -444,12 +444,12 @@
         activity.mConfigLatch = new CountDownLatch(1);
         activity.mTestLatch = new CountDownLatch(1);
 
-        ClientTransaction transaction = newTransaction(activityThread, null);
+        ClientTransaction transaction = newTransaction(activityThread);
         transaction.addCallback(ConfigurationChangeItem.obtain(
                 processConfigLandscape, DEVICE_ID_INVALID));
         appThread.scheduleTransaction(transaction);
 
-        transaction = newTransaction(activityThread, activity.getActivityToken());
+        transaction = newTransaction(activityThread);
         transaction.addCallback(ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigLandscape));
         transaction.addCallback(ConfigurationChangeItem.obtain(
@@ -829,7 +829,8 @@
         return thread.getActivityClient(token);
     }
 
-    private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
+    @NonNull
+    private static ClientTransaction newRelaunchResumeTransaction(@NonNull Activity activity) {
         final Configuration currentConfig = activity.getResources().getConfiguration();
         final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(
                 activity.getActivityToken(), null, null, 0,
@@ -846,7 +847,8 @@
         return transaction;
     }
 
-    private static ClientTransaction newResumeTransaction(Activity activity) {
+    @NonNull
+    private static ClientTransaction newResumeTransaction(@NonNull Activity activity) {
         final ResumeActivityItem resumeStateRequest =
                 ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */,
                         false /* shouldSendCompatFakeFocus */);
@@ -857,7 +859,8 @@
         return transaction;
     }
 
-    private static ClientTransaction newStopTransaction(Activity activity) {
+    @NonNull
+    private static ClientTransaction newStopTransaction(@NonNull Activity activity) {
         final StopActivityItem stopStateRequest = StopActivityItem.obtain(
                 activity.getActivityToken(), 0 /* configChanges */);
 
@@ -867,8 +870,9 @@
         return transaction;
     }
 
-    private static ClientTransaction newActivityConfigTransaction(Activity activity,
-            Configuration config) {
+    @NonNull
+    private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity,
+            @NonNull Configuration config) {
         final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), config);
 
@@ -878,8 +882,9 @@
         return transaction;
     }
 
-    private static ClientTransaction newNewIntentTransaction(Activity activity,
-            List<ReferrerIntent> intents, boolean resume) {
+    @NonNull
+    private static ClientTransaction newNewIntentTransaction(@NonNull Activity activity,
+            @NonNull List<ReferrerIntent> intents, boolean resume) {
         final NewIntentItem item = NewIntentItem.obtain(activity.getActivityToken(), intents,
                 resume);
 
@@ -889,13 +894,14 @@
         return transaction;
     }
 
-    private static ClientTransaction newTransaction(Activity activity) {
-        return newTransaction(activity.getActivityThread(), activity.getActivityToken());
+    @NonNull
+    private static ClientTransaction newTransaction(@NonNull Activity activity) {
+        return newTransaction(activity.getActivityThread());
     }
 
-    private static ClientTransaction newTransaction(ActivityThread activityThread,
-            @Nullable IBinder activityToken) {
-        return ClientTransaction.obtain(activityThread.getApplicationThread(), activityToken);
+    @NonNull
+    private static ClientTransaction newTransaction(@NonNull ActivityThread activityThread) {
+        return ClientTransaction.obtain(activityThread.getApplicationThread());
     }
 
     // Test activity
diff --git a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
index 08033cc..785a8a1 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
@@ -66,7 +66,7 @@
 
         final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
                 .obtain(mToken, mConfiguration);
-        final Context context = item.getContextToUpdate(mHandler, mToken);
+        final Context context = item.getContextToUpdate(mHandler);
 
         assertEquals(mActivity, context);
     }
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index 3d252fb..531404b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -21,7 +21,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.ClientTransactionHandler;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -46,22 +45,21 @@
 
     @Test
     public void testPreExecute() {
-        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class);
-        IBinder token = mock(IBinder.class);
+        final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+        final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+        final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+        final ClientTransactionHandler clientTransactionHandler =
+                mock(ClientTransactionHandler.class);
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
-                token /* activityToken */);
+        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(callback1);
         transaction.addCallback(callback2);
         transaction.setLifecycleStateRequest(stateRequest);
 
         transaction.preExecute(clientTransactionHandler);
 
-        verify(callback1, times(1)).preExecute(clientTransactionHandler, token);
-        verify(callback2, times(1)).preExecute(clientTransactionHandler, token);
-        verify(stateRequest, times(1)).preExecute(clientTransactionHandler, token);
+        verify(callback1, times(1)).preExecute(clientTransactionHandler);
+        verify(callback2, times(1)).preExecute(clientTransactionHandler);
+        verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
index 3926cfb..d9f5523 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
@@ -24,7 +24,6 @@
 import android.app.ClientTransactionHandler;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -49,8 +48,6 @@
 
     @Mock
     private ClientTransactionHandler mHandler;
-    @Mock
-    private IBinder mToken;
     // Can't mock final class.
     private final Configuration mConfiguration = new Configuration();
 
@@ -63,7 +60,7 @@
     public void testGetContextToUpdate() {
         final ConfigurationChangeItem item = ConfigurationChangeItem
                 .obtain(mConfiguration, DEVICE_ID_DEFAULT);
-        final Context context = item.getContextToUpdate(mHandler, mToken);
+        final Context context = item.getContextToUpdate(mHandler);
 
         assertEquals(ActivityThread.currentApplication(), context);
     }
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index c8d8f4b..4bbde0c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertSame;
 
 import android.app.ActivityOptions;
+import android.app.IApplicationThread;
 import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -41,8 +42,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.function.Supplier;
 
@@ -60,7 +64,15 @@
 @Presubmit
 public class ObjectPoolTests {
 
-    private final IBinder mActivityToken = new Binder();
+    @Mock
+    private IApplicationThread mApplicationThread;
+    @Mock
+    private IBinder mActivityToken;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
 
     // 1. Check if two obtained objects from pool are not the same.
     // 2. Check if the state of the object is cleared after recycling.
@@ -309,15 +321,15 @@
 
     @Test
     public void testRecycleClientTransaction() {
-        ClientTransaction emptyItem = ClientTransaction.obtain(null, null);
-        ClientTransaction item = ClientTransaction.obtain(null, new Binder());
+        ClientTransaction emptyItem = ClientTransaction.obtain(null);
+        ClientTransaction item = ClientTransaction.obtain(mApplicationThread);
         assertNotSame(item, emptyItem);
         assertNotEquals(item, emptyItem);
 
         item.recycle();
         assertEquals(item, emptyItem);
 
-        ClientTransaction item2 = ClientTransaction.obtain(null, new Binder());
+        ClientTransaction item2 = ClientTransaction.obtain(mApplicationThread);
         assertSame(item, item2);
         assertNotEquals(item2, emptyItem);
     }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index a998b26..a1a2bdb 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -230,8 +231,7 @@
         when(stateRequest.getActivityToken()).thenReturn(token);
         when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
-                token /* activityToken */);
+        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(callback1);
         transaction.addCallback(callback2);
         transaction.setLifecycleStateRequest(stateRequest);
@@ -240,8 +240,8 @@
         mExecutor.execute(transaction);
 
         InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
-        inOrder.verify(callback1).execute(eq(mTransactionHandler), eq(token), any());
-        inOrder.verify(callback2).execute(eq(mTransactionHandler), eq(token), any());
+        inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
+        inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
         inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
     }
 
@@ -254,8 +254,7 @@
 
         // An incoming destroy transaction enters binder thread (preExecute).
         final IBinder token = mock(IBinder.class);
-        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */,
-                token /* activityToken */);
+        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
         destroyTransaction.setLifecycleStateRequest(
                 DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
         destroyTransaction.preExecute(mTransactionHandler);
@@ -263,8 +262,7 @@
         assertEquals(1, mTransactionHandler.getActivitiesToBeDestroyed().size());
 
         // A previous queued launch transaction runs on main thread (execute).
-        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */,
-                token /* activityToken */);
+        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
         final LaunchActivityItem launchItem =
                 spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
         launchTransaction.addCallback(launchItem);
@@ -272,7 +270,7 @@
 
         // The launch transaction should not be executed because its token is in the
         // to-be-destroyed container.
-        verify(launchItem, never()).execute(any(), any(), any());
+        verify(launchItem, never()).execute(any(), any());
 
         // After the destroy transaction has been executed, the token should be removed.
         mExecutor.execute(destroyTransaction);
@@ -286,8 +284,7 @@
         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
 
         IBinder token = mock(IBinder.class);
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
-                token /* activityToken */);
+        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(postExecItem);
 
         // Verify resolution that should get to onPause
@@ -439,7 +436,7 @@
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
         final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token);
+        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(activityItem);
         when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
 
@@ -449,7 +446,7 @@
     @Test
     public void testActivityItemExecute() {
         final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token);
+        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
         when(activityItem.getActivityToken()).thenReturn(token);
@@ -504,12 +501,12 @@
         private StubItem() {
         }
 
-        private StubItem(Parcel in) {
+        private StubItem(@NonNull Parcel in) {
         }
 
         @Override
-        public void execute(ClientTransactionHandler client, IBinder token,
-                PendingTransactionActions pendingActions) {
+        public void execute(@NonNull ClientTransactionHandler client,
+                @NonNull PendingTransactionActions pendingActions) {
         }
 
         @Override
@@ -517,12 +514,11 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
         }
 
-        public static final Parcelable.Creator<StubItem> CREATOR =
-                new Parcelable.Creator<StubItem>() {
-            public StubItem createFromParcel(Parcel in) {
+        public static final Parcelable.Creator<StubItem> CREATOR = new Parcelable.Creator<>() {
+            public StubItem createFromParcel(@NonNull Parcel in) {
                 return new StubItem(in);
             }
 
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index abc5d6b..7d047c9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -284,9 +284,7 @@
         StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
                 78 /* configChanges */);
 
-        Binder activityToken = new Binder();
-
-        ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
+        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(callback1);
         transaction.addCallback(callback2);
         transaction.setLifecycleStateRequest(lifecycleRequest);
@@ -307,9 +305,7 @@
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
                 mActivityToken, config());
 
-        Binder activityToken = new Binder();
-
-        ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
+        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(callback1);
         transaction.addCallback(callback2);
 
@@ -328,9 +324,7 @@
         StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
                 78 /* configChanges */);
 
-        Binder activityToken = new Binder();
-
-        ClientTransaction transaction = ClientTransaction.obtain(null, activityToken);
+        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.setLifecycleStateRequest(lifecycleRequest);
 
         writeAndPrepareForReading(transaction);
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
index db76d26..a801a76 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
@@ -53,8 +53,6 @@
     @Mock
     private ClientTransactionHandler mHandler;
     @Mock
-    private IBinder mToken;
-    @Mock
     private PendingTransactionActions mPendingActions;
     @Mock
     private IBinder mClientToken;
@@ -72,7 +70,7 @@
     public void testExecute() {
         final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
                 .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
-        item.execute(mHandler, mToken, mPendingActions);
+        item.execute(mHandler, mPendingActions);
 
         verify(mHandler).handleWindowContextInfoChanged(mClientToken,
                 new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
@@ -84,7 +82,7 @@
 
         final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
                 .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
-        final Context context = item.getContextToUpdate(mHandler, mToken);
+        final Context context = item.getContextToUpdate(mHandler);
 
         assertEquals(mWindowContext, context);
     }
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
index 17e0ebc..cf9935f 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -45,8 +45,6 @@
     @Mock
     private ClientTransactionHandler mHandler;
     @Mock
-    private IBinder mToken;
-    @Mock
     private PendingTransactionActions mPendingActions;
     @Mock
     private IBinder mClientToken;
@@ -60,7 +58,7 @@
     public void testExecute() {
         final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
                 mClientToken);
-        item.execute(mHandler, mToken, mPendingActions);
+        item.execute(mHandler, mPendingActions);
 
         verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
     }
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 5dbeac2..407c6c3 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -26,6 +26,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class BroadcastReceiverTests {
@@ -47,15 +50,22 @@
     @Test
     public void testReceiverLimit() {
         final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction");
+        final List<EmptyReceiver> receivers = new ArrayList<>(RECEIVER_LIMIT_PER_APP);
         try {
             for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) {
-                mContext.registerReceiver(new EmptyReceiver(), mockFilter,
+                final EmptyReceiver receiver = new EmptyReceiver();
+                mContext.registerReceiver(receiver, mockFilter,
                         Context.RECEIVER_EXPORTED_UNAUDITED);
+                receivers.add(receiver);
             }
             fail("No exception thrown when registering "
                     + (RECEIVER_LIMIT_PER_APP + 1) + " receivers");
         } catch (IllegalStateException ise) {
             // Expected
+        } finally {
+            for (int i = receivers.size() - 1; i >= 0; i--) {
+                mContext.unregisterReceiver(receivers.remove(i));
+            }
         }
     }
 }
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index 5553902..37ef6cb 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -85,9 +85,11 @@
         assertEquals(2, cache.getAllServicesSize(U0));
         assertEquals(2, cache.getPersistentServicesSize(U0));
         assertNotEmptyFileCreated(cache, U0);
+        cache.unregisterReceivers();
         // Make sure all services can be loaded from xml
         cache = new TestServicesCache();
         assertEquals(2, cache.getPersistentServicesSize(U0));
+        cache.unregisterReceivers();
     }
 
     public void testGetAllServicesReplaceUid() {
@@ -110,6 +112,7 @@
         assertTrue("UID must be updated to the new value",
                 uids.contains(SYSTEM_IMAGE_UID));
         assertFalse("UID must be updated to the new value", uids.contains(UID2));
+        cache.unregisterReceivers();
     }
 
     public void testGetAllServicesServiceRemoved() {
@@ -118,6 +121,7 @@
         cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
         assertEquals(2, cache.getAllServicesSize(U0));
         assertEquals(2, cache.getPersistentServicesSize(U0));
+        cache.unregisterReceivers();
         // Re-read data from disk and verify services were saved
         cache = new TestServicesCache();
         assertEquals(2, cache.getPersistentServicesSize(U0));
@@ -125,6 +129,7 @@
         cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
         assertEquals(1, cache.getAllServicesSize(U0));
         assertEquals(1, cache.getPersistentServicesSize(U0));
+        cache.unregisterReceivers();
     }
 
     public void testGetAllServicesMultiUser() {
@@ -137,12 +142,14 @@
         assertEquals(1, cache.getAllServicesSize(U1));
         assertEquals(1, cache.getPersistentServicesSize(U1));
         assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
+        cache.unregisterReceivers();
         // Re-read data from disk and verify services were saved
         cache = new TestServicesCache();
         assertEquals(1, cache.getPersistentServicesSize(U0));
         assertEquals(1, cache.getPersistentServicesSize(U1));
         assertNotEmptyFileCreated(cache, U0);
         assertNotEmptyFileCreated(cache, U1);
+        cache.unregisterReceivers();
     }
 
     public void testOnRemove() {
@@ -158,6 +165,7 @@
         cache.clearServicesForQuerying();
         assertEquals(1, cache.getAllServicesSize(U0));
         assertEquals(0, cache.getAllServicesSize(U1));
+        cache.unregisterReceivers();
     }
 
     public void testMigration() {
@@ -186,10 +194,12 @@
         cache.addServiceForQuerying(0, r2, newServiceInfo(t2, 2));
         assertEquals(2, cache.getAllServicesSize(u0));
         assertEquals(0, cache.getAllServicesSize(u1));
+        cache.unregisterReceivers();
         // Re-read data from disk. Verify that services were saved and old file was ignored
         cache = new TestServicesCache();
         assertEquals(2, cache.getPersistentServicesSize(u0));
         assertEquals(0, cache.getPersistentServicesSize(u1));
+        cache.unregisterReceivers();
     }
 
     private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 9840e15..7c4136d 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -35,6 +35,7 @@
 
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -71,6 +72,7 @@
         deleteViaContentProvider(NAMESPACE, KEY2);
         deleteViaContentProvider(NAMESPACE, KEY3);
         DeviceConfig.clearAllLocalOverrides();
+        DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE);
     }
 
     /**
@@ -880,6 +882,7 @@
     }
 
     @Test
+    @Ignore("b/300174188")
     public void syncDisabling() throws Exception {
         Properties properties1 = new Properties.Builder(NAMESPACE)
                 .setString(KEY, VALUE)
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index fbf8a92..7ebda00 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksCoreTests",
       "options": [
diff --git a/core/tests/coretests/src/android/service/quicksettings/OWNERS b/core/tests/coretests/src/android/service/quicksettings/OWNERS
new file mode 100644
index 0000000..5665490
--- /dev/null
+++ b/core/tests/coretests/src/android/service/quicksettings/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/service/quicksettings/OWNERS
diff --git a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
index d28eeff..04af5d7 100644
--- a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
+++ b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java
@@ -28,10 +28,10 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -40,13 +40,15 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class TileServiceTest {
 
     @Mock
     private IQSService.Stub mIQSService;
 
+    private TestableLooper mTestableLooper;
+
     private IBinder mTileToken;
     private TileService mTileService;
     private Tile mTile;
@@ -55,6 +57,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        mTestableLooper = TestableLooper.get(this);
+
         mTileToken = new Binder();
         when(mIQSService.asBinder()).thenCallRealMethod();
         when(mIQSService.queryLocalInterface(anyString())).thenReturn(mIQSService);
@@ -72,6 +76,7 @@
         when(mIQSService.getTile(mTileToken)).thenThrow(new RemoteException());
 
         IBinder result = mTileService.onBind(intent);
+        mTestableLooper.processAllMessages();
         assertNull(result);
     }
 
@@ -83,6 +88,7 @@
         when(mIQSService.getTile(mTileToken)).thenReturn(null);
 
         IBinder result = mTileService.onBind(intent);
+        mTestableLooper.processAllMessages();
 
         assertNotNull(result);
         verify(mIQSService, never()).onStartSuccessful(any());
@@ -96,6 +102,7 @@
         when(mIQSService.getTile(mTileToken)).thenReturn(mTile);
 
         IBinder result = mTileService.onBind(intent);
+        mTestableLooper.processAllMessages();
 
         assertNotNull(result);
         verify(mIQSService).onStartSuccessful(mTileToken);
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 6cea2f3..dd8f73f 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -399,7 +399,7 @@
     @SmallTest
     public void testAutoLinkWebUrl_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld()
             throws Exception {
-        String url = "thank.you";
+        String url = "thank.unknowntld";
         assertFalse("Should not match URL that does not start with a protocol and " +
                 "does not contain a known TLD",
                 Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
@@ -422,7 +422,7 @@
     @SmallTest
     public void testAutoLinkWebUrl_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld()
             throws Exception {
-        String url = "Thank\u263A.you";
+        String url = "Thank\u263A.unknowntld";
         assertFalse("Should not match URLs containing emoji and with unknown TLD",
                 Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
     }
diff --git a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
index 60a0a2a..c210fd6 100644
--- a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
@@ -90,22 +90,73 @@
         assertGetChildLocalHitRegionEmpty(R.id.view_cover_top, R.id.view_cover_bottom);
     }
 
+    @Test
+    public void testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView() {
+        // In this case, two views overlap with each other and the MotionEvent is injected to the
+        // bottom view. It verifies that the hit region of the top view won't be blocked by the
+        // bottom view.
+        testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(/* isHover= */ true);
+        testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(/* isHover= */ false);
+    }
+
+    private void testGetChildLocalHitRegion_topViewIsNotBlockedByBottomView(boolean isHover) {
+        // In this case, two views overlap with each other and the MotionEvent is injected to the
+        // bottom view. It verifies that the hit region of the top view won't be blocked by the
+        // bottom view.
+        mScenarioRule.getScenario().onActivity(activity -> {
+            View viewTop = activity.findViewById(R.id.view_overlap_top);
+            View viewBottom = activity.findViewById(R.id.view_overlap_bottom);
+
+            // The viewTop covers the left side of the viewBottom. To avoid the MotionEvent gets
+            // blocked by viewTop, we inject MotionEvents into viewBottom's right bottom corner.
+            float x = viewBottom.getWidth() - 1;
+            float y = viewBottom.getHeight() - 1;
+            injectMotionEvent(viewBottom, x, y, isHover);
+
+            Matrix actualMatrix = new Matrix();
+            Region actualRegion = new Region(0, 0, viewTop.getWidth(), viewTop.getHeight());
+            boolean actualNotEmpty = viewTop.getParent()
+                    .getChildLocalHitRegion(viewTop, actualRegion, actualMatrix, isHover);
+
+            int[] windowLocation = new int[2];
+            viewTop.getLocationInWindow(windowLocation);
+            Matrix expectMatrix = new Matrix();
+            expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]);
+            // Though viewTop and viewBottom overlaps, viewTop's hit region won't be blocked by
+            // viewBottom.
+            Region expectRegion = new Region(0, 0, viewTop.getWidth(), viewTop.getHeight());
+
+            assertThat(actualNotEmpty).isTrue();
+            assertThat(actualMatrix).isEqualTo(expectMatrix);
+            assertThat(actualRegion).isEqualTo(expectRegion);
+        });
+    }
+
     private void injectMotionEvent(View view, boolean isHover) {
+        float x = view.getWidth() / 2f;
+        float y = view.getHeight() / 2f;
+        injectMotionEvent(view, x, y, isHover);
+    }
+
+    /**
+     * Inject MotionEvent into the given view, at the given location specified in the view's
+     * coordinates.
+     */
+    private void injectMotionEvent(View view, float x, float y, boolean isHover) {
         int[] location = new int[2];
         view.getLocationInWindow(location);
 
-        float x = location[0] + view.getWidth() / 2f;
-        float y = location[1] + view.getHeight() / 2f;
+        float globalX = location[0] + x;
+        float globalY = location[1] + y;
 
         int action = isHover ? MotionEvent.ACTION_HOVER_ENTER : MotionEvent.ACTION_DOWN;
         MotionEvent motionEvent = MotionEvent.obtain(/* downtime= */ 0, /* eventTime= */ 0, action,
-                x, y, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
+                globalX, globalY, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
                 /* xPrecision= */ 1, /* yPrecision= */ 1, /* deviceId= */0, /* edgeFlags= */0);
 
         View rootView = view.getRootView();
         rootView.dispatchPointerEvent(motionEvent);
     }
-
     private void assertGetChildLocalHitRegion(int viewId) {
         assertGetChildLocalHitRegion(viewId, /* isHover= */ true);
         assertGetChildLocalHitRegion(viewId, /* isHover= */ false);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
index 2eeaf53..1c9f04a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
@@ -19,8 +19,12 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
+import android.content.pm.ShortcutInfo
+import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.os.UserHandle
 import android.service.chooser.ChooserTarget
@@ -32,12 +36,15 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.internal.R
 import com.android.internal.app.ChooserListAdapter.LoadDirectShareIconTask
+import com.android.internal.app.chooser.DisplayResolveInfo
 import com.android.internal.app.chooser.SelectableTargetInfo
 import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
 import com.android.internal.app.chooser.TargetInfo
 import com.android.server.testutils.any
 import com.android.server.testutils.mock
 import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.anyInt
@@ -46,22 +53,25 @@
 
 @RunWith(AndroidJUnit4::class)
 class ChooserListAdapterTest {
-    private val packageManager = mock<PackageManager> {
-        whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
-    }
+    private val packageManager =
+        mock<PackageManager> { whenever(resolveActivity(any(), anyInt())).thenReturn(mock()) }
     private val context = InstrumentationRegistry.getInstrumentation().getContext()
     private val resolverListController = mock<ResolverListController>()
-    private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
-        whenever(maxRankedTargets).thenReturn(0)
-    }
-    private val selectableTargetInfoCommunicator =
-        mock<SelectableTargetInfoCommunicator> {
-            whenever(targetIntent).thenReturn(mock())
+    private val chooserListCommunicator =
+        mock<ChooserListAdapter.ChooserListCommunicator> {
+            whenever(maxRankedTargets).thenReturn(0)
         }
+    private val selectableTargetInfoCommunicator =
+        mock<SelectableTargetInfoCommunicator> { whenever(targetIntent).thenReturn(mock()) }
     private val chooserActivityLogger = mock<ChooserActivityLogger>()
 
+    @Before
+    fun setUp() {
+        whenever(resolverListController.userHandle).thenReturn(UserHandle.CURRENT)
+    }
+
     private fun createChooserListAdapter(
-        taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
+        taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask = createTaskProvider()
     ) =
         ChooserListAdapterOverride(
             context,
@@ -98,9 +108,8 @@
         view.tag = viewHolderOne
         val targetInfo = createSelectableTargetInfo()
         val iconTaskOne = mock<LoadDirectShareIconTask>()
-        val testTaskProvider = mock<() -> LoadDirectShareIconTask> {
-            whenever(invoke()).thenReturn(iconTaskOne)
-        }
+        val testTaskProvider =
+            mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
         val testSubject = createChooserListAdapter { testTaskProvider.invoke() }
         testSubject.testViewBind(view, targetInfo, 0)
 
@@ -114,6 +123,65 @@
         verify(testTaskProvider, times(1)).invoke()
     }
 
+    @Test
+    fun getServiceTargetCount_shouldNotShowServiceTargets_returnsZero() {
+        whenever(chooserListCommunicator.shouldShowServiceTargets()).thenReturn(false)
+        val adapter = createChooserListAdapter()
+        whenever(chooserListCommunicator.maxRankedTargets).thenReturn(10)
+        addServiceTargets(adapter, targetCount = 50)
+
+        assertThat(adapter.serviceTargetCount).isEqualTo(0)
+    }
+
+    private fun createTaskProvider(): (SelectableTargetInfo?) -> LoadDirectShareIconTask {
+        val iconTaskOne = mock<LoadDirectShareIconTask>()
+        val testTaskProvider =
+            mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
+        return { testTaskProvider.invoke() }
+    }
+
+    private fun addServiceTargets(adapter: ChooserListAdapter, targetCount: Int) {
+        val origTarget =
+            DisplayResolveInfo(
+                Intent(),
+                createResolveInfo(),
+                Intent(),
+                ResolverListAdapter.ResolveInfoPresentationGetter(context, 200, createResolveInfo())
+            )
+        val targets = mutableListOf<ChooserTarget>()
+        for (i in 1..targetCount) {
+            val score = 1f
+            val componentName = ComponentName("chooser.list.adapter", "Test$i")
+            val extras = Bundle()
+            val icon: Icon? = null
+            targets += ChooserTarget("Title $i", icon, score, componentName, extras)
+        }
+        val directShareToShortcutInfos = mapOf<ChooserTarget, ShortcutInfo>()
+        adapter.addServiceResults(
+            origTarget,
+            targets,
+            ChooserActivity.TARGET_TYPE_DEFAULT,
+            directShareToShortcutInfos
+        )
+    }
+
+    private fun createResolveInfo(): ResolveInfo {
+        val applicationInfo =
+            ApplicationInfo().apply {
+                packageName = "chooser.list.adapter"
+                name = "ChooserListAdapterTestApplication"
+            }
+        val activityInfo =
+            ActivityInfo().apply {
+                packageName = applicationInfo.packageName
+                name = "ChooserListAdapterTest"
+            }
+        activityInfo.applicationInfo = applicationInfo
+        val resolveInfo = ResolveInfo()
+        resolveInfo.activityInfo = activityInfo
+        return resolveInfo
+    }
+
     private fun createSelectableTargetInfo(): SelectableTargetInfo =
         SelectableTargetInfo(
             context,
@@ -125,13 +193,7 @@
         )
 
     private fun createChooserTarget(): ChooserTarget =
-        ChooserTarget(
-            "Title",
-            null,
-            1f,
-            ComponentName("package", "package.Class"),
-            Bundle()
-        )
+        ChooserTarget("Title", null, 1f, ComponentName("package", "package.Class"), Bundle())
 
     private fun createView(): View {
         val view = FrameLayout(context)
@@ -164,23 +226,23 @@
     chooserActivityLogger: ChooserActivityLogger?,
     initialIntentsUserHandle: UserHandle?,
     private val taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
-) : ChooserListAdapter(
-    context,
-    payloadIntents,
-    initialIntents,
-    rList,
-    filterLastUsed,
-    resolverListController,
-    chooserListCommunicator,
-    selectableTargetInfoCommunicator,
-    packageManager,
-    chooserActivityLogger,
-    initialIntentsUserHandle,
-) {
+) :
+    ChooserListAdapter(
+        context,
+        payloadIntents,
+        initialIntents,
+        rList,
+        filterLastUsed,
+        resolverListController,
+        chooserListCommunicator,
+        selectableTargetInfoCommunicator,
+        packageManager,
+        chooserActivityLogger,
+        initialIntentsUserHandle,
+    ) {
     override fun createLoadDirectShareIconTask(
         info: SelectableTargetInfo?
-    ): LoadDirectShareIconTask =
-        taskProvider.invoke(info)
+    ): LoadDirectShareIconTask = taskProvider.invoke(info)
 
     fun testViewBind(view: View?, info: TargetInfo?, position: Int) {
         onBindView(view, info, position)
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java
new file mode 100644
index 0000000..4aca854
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.os.UserHandle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class ResolverAppPredictorCallbackTest {
+    private class Callback implements Consumer<List<AppTarget>> {
+        public int count = 0;
+        public List<AppTarget> latest = null;
+        @Override
+        public void accept(List<AppTarget> appTargets) {
+            count++;
+            latest = appTargets;
+        }
+    };
+
+    @Test
+    public void testAsConsumer() {
+        Callback callback = new Callback();
+        ResolverAppPredictorCallback wrapped = new ResolverAppPredictorCallback(callback);
+        assertThat(callback.count).isEqualTo(0);
+
+        List<AppTarget> targets = createAppTargetList();
+        wrapped.asConsumer().accept(targets);
+
+        assertThat(callback.count).isEqualTo(1);
+        assertThat(callback.latest).isEqualTo(targets);
+
+        wrapped.destroy();
+
+        // Shouldn't do anything:
+        wrapped.asConsumer().accept(targets);
+
+        assertThat(callback.count).isEqualTo(1);
+    }
+
+    @Test
+    public void testAsCallback() {
+        Callback callback = new Callback();
+        ResolverAppPredictorCallback wrapped = new ResolverAppPredictorCallback(callback);
+        assertThat(callback.count).isEqualTo(0);
+
+        List<AppTarget> targets = createAppTargetList();
+        wrapped.asCallback().onTargetsAvailable(targets);
+
+        assertThat(callback.count).isEqualTo(1);
+        assertThat(callback.latest).isEqualTo(targets);
+
+        wrapped.destroy();
+
+        // Shouldn't do anything:
+        wrapped.asConsumer().accept(targets);
+
+        assertThat(callback.count).isEqualTo(1);
+    }
+
+    @Test
+    public void testAsConsumer_null() {
+        Callback callback = new Callback();
+        ResolverAppPredictorCallback wrapped = new ResolverAppPredictorCallback(callback);
+        assertThat(callback.count).isEqualTo(0);
+
+        wrapped.asConsumer().accept(null);
+
+        assertThat(callback.count).isEqualTo(1);
+        assertThat(callback.latest).isEmpty();
+
+        wrapped.destroy();
+
+        // Shouldn't do anything:
+        wrapped.asConsumer().accept(null);
+
+        assertThat(callback.count).isEqualTo(1);
+    }
+
+    private List<AppTarget> createAppTargetList() {
+        AppTarget.Builder builder = new AppTarget.Builder(
+                new AppTargetId("ID"), "package", UserHandle.CURRENT);
+        return List.of(builder.build());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
index e870d60..8d825e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
@@ -99,6 +99,7 @@
                     if (isCharging(intent) == mExpectedChargingState) {
                         mReady.open();
                     }
+                    context.unregisterReceiver(this);
                 }
             }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
         }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3206dd2..69aa401 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -235,6 +235,7 @@
 
     <privapp-permissions package="com.android.shell">
         <!-- Needed for test only -->
+        <permission name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"/>
         <permission name="android.permission.MANAGE_HEALTH_DATA"/>
         <permission name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"/>
         <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ee2eacf..e46ba9a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -253,12 +253,6 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1883484959": {
-      "message": "Content Recording: Display %d state is now (%d), so update recording?",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_CONTENT_RECORDING",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "-1872288685": {
       "message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s",
       "level": "VERBOSE",
@@ -445,6 +439,12 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
     },
+    "-1700778361": {
+      "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "-1699018375": {
       "message": "Adding activity %s to task %s callers: %s",
       "level": "INFO",
@@ -649,12 +649,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "-1480264178": {
-      "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_CONTENT_RECORDING",
-      "at": "com\/android\/server\/wm\/ContentRecorder.java"
-    },
     "-1478175541": {
       "message": "No longer animating wallpaper targets!",
       "level": "VERBOSE",
@@ -1855,12 +1849,6 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/Task.java"
     },
-    "-452750194": {
-      "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d.",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_CONTENT_RECORDING",
-      "at": "com\/android\/server\/wm\/ContentRecorder.java"
-    },
     "-451552570": {
       "message": "Current focused window being animated by recents. Overriding back callback to recents controller callback.",
       "level": "DEBUG",
@@ -2071,12 +2059,6 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
       "at": "com\/android\/server\/wm\/TransitionController.java"
     },
-    "-262984451": {
-      "message": "Relaunch failed %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-251259736": {
       "message": "No longer freezing: %s",
       "level": "VERBOSE",
@@ -2377,6 +2359,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
+    "34106798": {
+      "message": "Content Recording: Display %d state was (%d), is now (%d), so update recording?",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "34682671": {
       "message": "Not moving display (displayId=%d) to top. Top focused displayId=%d. Reason: FLAG_STEAL_TOP_FOCUS_DISABLED",
       "level": "INFO",
@@ -2413,6 +2401,12 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "61363198": {
+      "message": "Auto-PIP allowed, requesting PIP mode via requestStartTransition(): %s, willAutoPip: %b",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "74885950": {
       "message": "Waiting for top state to be released by %s",
       "level": "VERBOSE",
@@ -2425,12 +2419,6 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
-    "90764070": {
-      "message": "Could not report token removal to the window token client.",
-      "level": "WARN",
-      "group": "WM_ERROR",
-      "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
-    },
     "95216706": {
       "message": "hideIme target: %s ",
       "level": "DEBUG",
@@ -3079,12 +3067,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "643263584": {
-      "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop %d x %d for display %d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_CONTENT_RECORDING",
-      "at": "com\/android\/server\/wm\/ContentRecorder.java"
-    },
     "644675193": {
       "message": "Real start recents",
       "level": "DEBUG",
@@ -4105,6 +4087,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1687944543": {
+      "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s, since the surface is not available.",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "1699269281": {
       "message": "Don't organize or trigger events for untrusted displayId=%d",
       "level": "WARN",
@@ -4333,6 +4321,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "1936800105": {
+      "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "1945495497": {
       "message": "Focused window didn't have a valid surface drawn.",
       "level": "DEBUG",
@@ -4351,12 +4345,6 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
-    "1948483534": {
-      "message": "Could not report config changes to the window token client.",
-      "level": "WARN",
-      "group": "WM_ERROR",
-      "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
-    },
     "1964565370": {
       "message": "Starting remote animation",
       "level": "INFO",
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 919a93b..0f3488b 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.StrictMode;
 import android.security.maintenance.IKeystoreMaintenance;
 import android.system.keystore2.Domain;
 import android.system.keystore2.KeyDescriptor;
@@ -51,6 +52,7 @@
      * @hide
      */
     public static int onUserAdded(@NonNull int userId) {
+        StrictMode.noteDiskWrite();
         try {
             getService().onUserAdded(userId);
             return 0;
@@ -71,6 +73,7 @@
      * @hide
      */
     public static int onUserRemoved(int userId) {
+        StrictMode.noteDiskWrite();
         try {
             getService().onUserRemoved(userId);
             return 0;
@@ -93,6 +96,7 @@
      * @hide
      */
     public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
+        StrictMode.noteDiskWrite();
         try {
             getService().onUserPasswordChanged(userId, password);
             return 0;
@@ -110,6 +114,7 @@
      * be cleared.
      */
     public static int clearNamespace(@Domain int domain, long namespace) {
+        StrictMode.noteDiskWrite();
         try {
             getService().clearNamespace(domain, namespace);
             return 0;
@@ -129,6 +134,7 @@
      * @return UserState enum variant as integer if successful or an error
      */
     public static int getState(int userId) {
+        StrictMode.noteDiskRead();
         try {
             return getService().getState(userId);
         } catch (ServiceSpecificException e) {
@@ -144,6 +150,7 @@
      * Informs Keystore 2.0 that an off body event was detected.
      */
     public static void onDeviceOffBody() {
+        StrictMode.noteDiskWrite();
         try {
             getService().onDeviceOffBody();
         } catch (Exception e) {
@@ -172,6 +179,7 @@
      *         * SYSTEM_ERROR if an unexpected error occurred.
      */
     public static int migrateKeyNamespace(KeyDescriptor source, KeyDescriptor destination) {
+        StrictMode.noteDiskWrite();
         try {
             getService().migrateKeyNamespace(source, destination);
             return 0;
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 00219e7..2d2dd24 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -22,6 +22,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.StrictMode;
 import android.security.authorization.IKeystoreAuthorization;
 import android.security.authorization.LockScreenEvent;
 import android.system.keystore2.ResponseCode;
@@ -48,6 +49,7 @@
      * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
      */
     public static int addAuthToken(@NonNull HardwareAuthToken authToken) {
+        StrictMode.noteSlowCall("addAuthToken");
         try {
             getService().addAuthToken(authToken);
             return 0;
@@ -81,6 +83,7 @@
      */
     public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
             @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
+        StrictMode.noteDiskWrite();
         try {
             if (locked) {
                 getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8811a7f..8045f55 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -18,6 +18,7 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.os.StrictMode;
 import android.os.UserHandle;
 import android.security.maintenance.UserState;
 
@@ -126,6 +127,8 @@
      * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
      */
     public int addAuthToken(byte[] authToken) {
+        StrictMode.noteDiskWrite();
+
         return Authorization.addAuthToken(authToken);
     }
 
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index c83f298..5e16bce 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.StrictMode;
 import android.security.keymaster.KeymasterDefs;
 import android.system.keystore2.Domain;
 import android.system.keystore2.IKeystoreService;
@@ -148,6 +149,8 @@
     }
 
     void delete(KeyDescriptor descriptor) throws KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         handleRemoteExceptionWithRetry((service) -> {
             service.deleteKey(descriptor);
             return 0;
@@ -158,6 +161,8 @@
      * List all entries in the keystore for in the given namespace.
      */
     public KeyDescriptor[] list(int domain, long namespace) throws KeyStoreException {
+        StrictMode.noteDiskRead();
+
         return handleRemoteExceptionWithRetry((service) -> service.listEntries(domain, namespace));
     }
 
@@ -166,6 +171,8 @@
      */
     public KeyDescriptor[] listBatch(int domain, long namespace, String startPastAlias)
             throws KeyStoreException {
+        StrictMode.noteDiskRead();
+
         return handleRemoteExceptionWithRetry(
                 (service) -> service.listEntriesBatched(domain, namespace, startPastAlias));
     }
@@ -228,6 +235,8 @@
      */
     public KeyDescriptor grant(KeyDescriptor descriptor, int granteeUid, int accessVector)
             throws  KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         return handleRemoteExceptionWithRetry(
                 (service) -> service.grant(descriptor, granteeUid, accessVector)
         );
@@ -243,6 +252,8 @@
      */
     public void ungrant(KeyDescriptor descriptor, int granteeUid)
             throws KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         handleRemoteExceptionWithRetry((service) -> {
             service.ungrant(descriptor, granteeUid);
             return 0;
@@ -259,6 +270,8 @@
      */
     public KeyEntryResponse getKeyEntry(@NonNull KeyDescriptor descriptor)
             throws KeyStoreException {
+        StrictMode.noteDiskRead();
+
         return handleRemoteExceptionWithRetry((service) -> service.getKeyEntry(descriptor));
     }
 
@@ -290,6 +303,8 @@
      */
     public void updateSubcomponents(@NonNull KeyDescriptor key, byte[] publicCert,
             byte[] publicCertChain) throws KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         handleRemoteExceptionWithRetry((service) -> {
             service.updateSubcomponent(key, publicCert, publicCertChain);
             return 0;
@@ -305,6 +320,8 @@
      */
     public void deleteKey(@NonNull KeyDescriptor descriptor)
             throws KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         handleRemoteExceptionWithRetry((service) -> {
             service.deleteKey(descriptor);
             return 0;
@@ -315,6 +332,8 @@
      * Returns the number of Keystore entries for a given domain and namespace.
      */
     public int getNumberOfEntries(int domain, long namespace) throws KeyStoreException {
+        StrictMode.noteDiskRead();
+
         return handleRemoteExceptionWithRetry((service)
                 -> service.getNumberOfEntries(domain, namespace));
     }
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 737ff2b..7c9b8eb 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -21,6 +21,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.os.StrictMode;
 import android.security.keymaster.KeymasterDefs;
 import android.system.keystore2.IKeystoreOperation;
 import android.system.keystore2.ResponseCode;
@@ -97,6 +98,7 @@
      * @throws KeyStoreException
      */
     public void updateAad(@NonNull byte[] input) throws KeyStoreException {
+        StrictMode.noteSlowCall("updateAad");
         handleExceptions(() -> {
             mOperation.updateAad(input);
             return 0;
@@ -112,6 +114,7 @@
      * @hide
      */
     public byte[] update(@NonNull byte[] input) throws KeyStoreException {
+        StrictMode.noteSlowCall("update");
         return handleExceptions(() -> mOperation.update(input));
     }
 
@@ -125,6 +128,7 @@
      * @hide
      */
     public byte[] finish(byte[] input, byte[] signature) throws KeyStoreException {
+        StrictMode.noteSlowCall("finish");
         return handleExceptions(() -> mOperation.finish(input, signature));
     }
 
@@ -135,6 +139,7 @@
      * @hide
      */
     public void abort() throws KeyStoreException {
+        StrictMode.noteSlowCall("abort");
         handleExceptions(() -> {
             mOperation.abort();
             return 0;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 9c0b46c..6ab148a 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -22,6 +22,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.os.StrictMode;
 import android.security.keystore.BackendBusyException;
 import android.security.keystore.KeyStoreConnectException;
 import android.system.keystore2.AuthenticatorSpec;
@@ -75,6 +76,7 @@
      */
     public KeyStoreOperation createOperation(@NonNull KeyDescriptor keyDescriptor,
             Collection<KeyParameter> args) throws KeyStoreException {
+        StrictMode.noteDiskWrite();
         while (true) {
             try {
                 CreateOperationResponse createOperationResponse =
@@ -142,6 +144,8 @@
     public KeyMetadata generateKey(@NonNull KeyDescriptor descriptor, KeyDescriptor attestationKey,
             Collection<KeyParameter> args, int flags, byte[] entropy)
             throws KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         return handleExceptions(() -> mSecurityLevel.generateKey(
                 descriptor, attestationKey, args.toArray(new KeyParameter[args.size()]),
                 flags, entropy));
@@ -163,6 +167,8 @@
     public KeyMetadata importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey,
             Collection<KeyParameter> args, int flags, byte[] keyData)
             throws KeyStoreException {
+        StrictMode.noteDiskWrite();
+
         return handleExceptions(() -> mSecurityLevel.importKey(descriptor, attestationKey,
                 args.toArray(new KeyParameter[args.size()]), flags, keyData));
     }
@@ -186,6 +192,7 @@
             @NonNull byte[] wrappedKey, byte[] maskingKey,
             Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)
             throws KeyStoreException {
+        StrictMode.noteDiskWrite();
         KeyDescriptor keyDescriptor = new KeyDescriptor();
         keyDescriptor.alias = wrappedKeyDescriptor.alias;
         keyDescriptor.nspace = wrappedKeyDescriptor.nspace;
diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java
index c85b6b1..0cc4dfa 100644
--- a/keystore/java/android/security/LegacyVpnProfileStore.java
+++ b/keystore/java/android/security/LegacyVpnProfileStore.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.StrictMode;
 import android.security.legacykeystore.ILegacyKeystore;
 import android.util.Log;
 
@@ -51,6 +52,7 @@
      * @hide
      */
     public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
+        StrictMode.noteDiskWrite();
         try {
             getService().put(alias, ILegacyKeystore.UID_SELF, profile);
             return true;
@@ -70,6 +72,7 @@
      * @hide
      */
     public static byte[] get(@NonNull String alias) {
+        StrictMode.noteDiskRead();
         try {
             return getService().get(alias, ILegacyKeystore.UID_SELF);
         } catch (ServiceSpecificException e) {
@@ -89,6 +92,7 @@
      * @hide
      */
     public static boolean remove(@NonNull String alias) {
+        StrictMode.noteDiskWrite();
         try {
             getService().remove(alias, ILegacyKeystore.UID_SELF);
             return true;
@@ -109,6 +113,7 @@
      * @hide
      */
     public static @NonNull String[] list(@NonNull String prefix) {
+        StrictMode.noteDiskRead();
         try {
             final String[] aliases = getService().list(prefix, ILegacyKeystore.UID_SELF);
             for (int i = 0; i < aliases.length; ++i) {
diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java
index e07eaa2..d481a07 100644
--- a/keystore/java/android/security/SystemKeyStore.java
+++ b/keystore/java/android/security/SystemKeyStore.java
@@ -18,6 +18,9 @@
 
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.StrictMode;
+
+import libcore.io.IoUtils;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -28,8 +31,6 @@
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
 
-import libcore.io.IoUtils;
-
 /**
  *@hide
  */
@@ -69,6 +70,7 @@
 
     public byte[] generateNewKey(int numBits, String algName, String keyName)
             throws NoSuchAlgorithmException {
+        StrictMode.noteDiskWrite();
 
         // Check if key with similar name exists. If so, return null.
         File keyFile = getKeyFile(keyName);
@@ -103,6 +105,7 @@
     }
 
     private File getKeyFile(String keyName) {
+        StrictMode.noteDiskWrite();
         File sysKeystoreDir = new File(Environment.getDataDirectory(),
                 SYSTEM_KEYSTORE_DIRECTORY);
         File keyFile = new File(sysKeystoreDir, keyName + KEY_FILE_EXTENSION);
@@ -114,6 +117,7 @@
     }
 
     public byte[] retrieveKey(String keyName) throws IOException {
+        StrictMode.noteDiskRead();
         File keyFile = getKeyFile(keyName);
         if (!keyFile.exists()) {
             return null;
@@ -122,6 +126,7 @@
     }
 
     public void deleteKey(String keyName) {
+        StrictMode.noteDiskWrite();
 
         // Get the file first.
         File keyFile = getKeyFile(keyName);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index d129891..9ac0f6d 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.security.keymint.KeyParameter;
+import android.os.StrictMode;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
@@ -137,6 +138,7 @@
         if (!(key instanceof AndroidKeyStorePrivateKey)
                 && (key instanceof PrivateKey || key instanceof PublicKey)) {
             try {
+                StrictMode.noteSlowCall("engineInit");
                 mCipher = Cipher.getInstance(getTransform());
                 String transform = getTransform();
 
@@ -203,6 +205,7 @@
         if (!(key instanceof AndroidKeyStorePrivateKey)
                 && (key instanceof PrivateKey || key instanceof PublicKey)) {
             try {
+                StrictMode.noteSlowCall("engineInit");
                 mCipher = Cipher.getInstance(getTransform());
                 mCipher.init(opmode, key, params, random);
                 return;
@@ -233,6 +236,7 @@
         if (!(key instanceof AndroidKeyStorePrivateKey)
                 && (key instanceof PrivateKey || key instanceof PublicKey)) {
             try {
+                StrictMode.noteSlowCall("engineInit");
                 mCipher = Cipher.getInstance(getTransform());
                 mCipher.init(opmode, key, params, random);
                 return;
@@ -346,6 +350,7 @@
         parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
 
         try {
+            StrictMode.noteDiskRead();
             mOperation = mKey.getSecurityLevel().createOperation(
                     mKey.getKeyIdDescriptor(),
                     parameters
@@ -521,6 +526,7 @@
     @Override
     protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
         if (mCipher != null) {
+            StrictMode.noteSlowCall("engineUpdateAAD");
             mCipher.updateAAD(input, inputOffset, inputLen);
             return;
         }
@@ -562,6 +568,7 @@
     @Override
     protected final void engineUpdateAAD(ByteBuffer src) {
         if (mCipher != null) {
+            StrictMode.noteSlowCall("engineUpdateAAD");
             mCipher.updateAAD(src);
             return;
         }
@@ -715,6 +722,7 @@
             throw new NullPointerException("key == null");
         }
         byte[] encoded = null;
+        StrictMode.noteSlowCall("engineWrap");
         if (key instanceof SecretKey) {
             if ("RAW".equalsIgnoreCase(key.getFormat())) {
                 encoded = key.getEncoded();
@@ -807,6 +815,7 @@
             throw new InvalidKeyException("Failed to unwrap key", e);
         }
 
+        StrictMode.noteSlowCall("engineUnwrap");
         switch (wrappedKeyType) {
             case Cipher.SECRET_KEY:
             {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index ace2053..9d3fca8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -195,7 +195,7 @@
     protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
         if (!ACCEPTED_SIGNING_SCHEMES.contains(key.getAlgorithm().toLowerCase())) {
             throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
-                    + ". Only" + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray())
+                    + ". Only " + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray())
                     + " supported");
         }
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
index 7292cd3..66e9f71 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -20,6 +20,7 @@
 import android.hardware.security.keymint.KeyParameter;
 import android.hardware.security.keymint.KeyPurpose;
 import android.hardware.security.keymint.Tag;
+import android.os.StrictMode;
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keystore.KeyStoreCryptoOperation;
@@ -174,6 +175,7 @@
         }
         byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
 
+        StrictMode.noteSlowCall("engineGenerateSecret");
         try {
             return mOperation.finish(otherPartyKeyEncoded, null);
         } catch (KeyStoreException e) {
@@ -245,6 +247,7 @@
                 Tag.PURPOSE, KeyPurpose.AGREE_KEY
         ));
 
+        StrictMode.noteDiskWrite();
         try {
             mOperation =
                     mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index f1681ec..d283b05 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -18,6 +18,7 @@
 
 import android.hardware.security.keymint.KeyParameter;
 import android.hardware.security.keymint.SecurityLevel;
+import android.os.StrictMode;
 import android.security.KeyStore2;
 import android.security.KeyStoreSecurityLevel;
 import android.security.keymaster.KeymasterDefs;
@@ -281,6 +282,7 @@
 
     @Override
     protected SecretKey engineGenerateKey() {
+        StrictMode.noteSlowCall("engineGenerateKey");
         KeyGenParameterSpec spec = mSpec;
         if (spec == null) {
             throw new IllegalStateException("Not initialized");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 474b7ea..1398da3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -27,6 +27,7 @@
 import android.hardware.security.keymint.SecurityLevel;
 import android.hardware.security.keymint.Tag;
 import android.os.Build;
+import android.os.StrictMode;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore2;
 import android.security.KeyStoreException;
@@ -617,6 +618,7 @@
 
     @Override
     public KeyPair generateKeyPair() {
+        StrictMode.noteSlowCall("generateKeyPair");
         if (mKeyStore == null || mSpec == null) {
             throw new IllegalStateException("Not initialized");
         }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 931c2f8..d5fb49a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -189,7 +189,7 @@
     protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
         if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
             throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
-                    + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+                    + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
         }
         super.initKey(key);
     }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index b4d8def..273dff1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -24,6 +24,7 @@
 import android.hardware.security.keymint.HardwareAuthenticatorType;
 import android.hardware.security.keymint.KeyParameter;
 import android.hardware.security.keymint.SecurityLevel;
+import android.os.StrictMode;
 import android.security.GateKeeper;
 import android.security.KeyStore2;
 import android.security.KeyStoreParameter;
@@ -164,6 +165,7 @@
         KeyDescriptor descriptor = makeKeyDescriptor(alias);
 
         try {
+            StrictMode.noteDiskRead();
             return mKeyStore.getKeyEntry(descriptor);
         } catch (android.security.KeyStoreException e) {
             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
@@ -447,6 +449,7 @@
             assertCanReplace(alias, targetDomain, mNamespace, descriptor);
 
             try {
+                StrictMode.noteDiskWrite();
                 mKeyStore.updateSubcomponents(
                         ((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
                         userCertBytes, chainBytes);
@@ -597,6 +600,7 @@
                     importArgs, flags, pkcs8EncodedPrivateKeyBytes);
 
             try {
+                StrictMode.noteDiskWrite();
                 mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
             } catch (android.security.KeyStoreException e) {
                 mKeyStore.deleteKey(metadata.key);
@@ -938,6 +942,7 @@
 
         KeyEntryResponse response = null;
         try {
+            StrictMode.noteDiskRead();
             response = mKeyStore.getKeyEntry(wrappingkey);
         } catch (android.security.KeyStoreException e) {
             throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
@@ -994,6 +999,7 @@
         }
 
         try {
+            StrictMode.noteDiskWrite();
             securityLevel.importWrappedKey(
                     wrappedKey, wrappingkey,
                     entry.getWrappedKeyBytes(),
@@ -1054,6 +1060,7 @@
         }
 
         try {
+            StrictMode.noteDiskWrite();
             mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
                     null /* publicCert - unused when used as pure certificate store. */,
                     encoded);
@@ -1066,6 +1073,7 @@
     public void engineDeleteEntry(String alias) throws KeyStoreException {
         KeyDescriptor descriptor = makeKeyDescriptor(alias);
         try {
+            StrictMode.noteDiskWrite();
             mKeyStore.deleteKey(descriptor);
         } catch (android.security.KeyStoreException e) {
             if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
@@ -1076,6 +1084,7 @@
 
     private KeyDescriptor[] getAliasesBatch(String startPastAlias) {
         try {
+            StrictMode.noteDiskRead();
             return mKeyStore.listBatch(
                     getTargetDomain(),
                     mNamespace,
@@ -1103,6 +1112,7 @@
     @Override
     public int engineSize() {
         try {
+            StrictMode.noteDiskRead();
             return mKeyStore.getNumberOfEntries(
                     getTargetDomain(),
                     mNamespace
@@ -1166,6 +1176,7 @@
 
         KeyDescriptor[] keyDescriptors = null;
         try {
+            StrictMode.noteDiskRead();
             keyDescriptors = mKeyStore.list(
                     getTargetDomain(),
                     mNamespace
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 0112e32..15d14e8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -213,9 +213,6 @@
             if (mRearDisplayStateRequest != null || isRearDisplayActive()) {
                 mRearDisplayStateRequest = null;
                 mDeviceStateManager.cancelStateRequest();
-            } else {
-                throw new IllegalStateException(
-                        "Unable to cancel a rear display session as there is no active session");
             }
         }
     }
@@ -432,10 +429,6 @@
         synchronized (mLock) {
             if (mRearDisplayPresentationController != null) {
                 mDeviceStateManager.cancelStateRequest();
-            } else {
-                throw new IllegalStateException(
-                        "Unable to cancel a rear display presentation session as there is no "
-                                + "active session");
             }
         }
     }
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 a5ee19e..cdfc4c8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -879,14 +879,12 @@
 
         // Skip resolving if the activity is on a pinned TaskFragmentContainer.
         // TODO(b/243518738): skip resolving for overlay container.
-        if (container != null) {
-            final TaskContainer taskContainer = container.getTaskContainer();
-            if (taskContainer.isTaskFragmentContainerPinned(container)) {
-                return true;
-            }
+        final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
+        if (container != null && taskContainer != null
+                && taskContainer.isTaskFragmentContainerPinned(container)) {
+            return true;
         }
 
-        final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
         if (!isOnReparent && taskContainer != null
                 && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
                         != container) {
@@ -895,6 +893,28 @@
             return true;
         }
 
+        // Ensure the top TaskFragments are updated to the right config if activity is resolved
+        // to a new TaskFragment while pin TF exists.
+        final boolean handled = resolveActivityToContainerByRule(wct, activity, container,
+                isOnReparent);
+        if (handled && taskContainer != null) {
+            final SplitPinContainer splitPinContainer = taskContainer.getSplitPinContainer();
+            if (splitPinContainer != null) {
+                final TaskFragmentContainer resolvedContainer = getContainerWithActivity(activity);
+                if (resolvedContainer != null && resolvedContainer.getRunningActivityCount() <= 1) {
+                    updateContainer(wct, splitPinContainer.getSecondaryContainer());
+                }
+            }
+        }
+        return handled;
+    }
+
+    /**
+     * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
+     */
+    boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
+            @NonNull Activity activity, @Nullable TaskFragmentContainer container,
+            boolean isOnReparent) {
         /*
          * We will check the following to see if there is any embedding rule matched:
          * 1. Whether the new launched activity should always expand.
@@ -1301,6 +1321,26 @@
             }
         }
 
+        // Ensure the top TaskFragments are updated to the right config if the intent is resolved
+        // to a new TaskFragment while pin TF exists.
+        final TaskFragmentContainer launchingContainer = resolveStartActivityIntentByRule(wct,
+                taskId, intent, launchingActivity);
+        if (launchingContainer != null && launchingContainer.getRunningActivityCount() == 0) {
+            final SplitPinContainer splitPinContainer =
+                    launchingContainer.getTaskContainer().getSplitPinContainer();
+            if (splitPinContainer != null) {
+                updateContainer(wct, splitPinContainer.getSecondaryContainer());
+            }
+        }
+        return launchingContainer;
+    }
+
+    /**
+     * Resolves the intent to a {@link TaskFragmentContainer} according to the Split-rules.
+     */
+    @Nullable
+    TaskFragmentContainer resolveStartActivityIntentByRule(@NonNull WindowContainerTransaction wct,
+            int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
         /*
          * We will check the following to see if there is any embedding rule matched:
          * 1. Whether the new activity intent should always expand.
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index e9abc7e..c72a42c 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -158,6 +158,7 @@
         "kotlinx-coroutines-android",
         "kotlinx-coroutines-core",
         "iconloader_base",
+        "com_android_wm_shell_flags_lib",
         "WindowManager-Shell-proto",
         "dagger2",
         "jsr330",
diff --git a/libs/WindowManager/Shell/aconfig/Android.bp b/libs/WindowManager/Shell/aconfig/Android.bp
new file mode 100644
index 0000000..1a98ffc
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/Android.bp
@@ -0,0 +1,12 @@
+aconfig_declarations {
+    name: "com_android_wm_shell_flags",
+    package: "com.android.wm.shell",
+    srcs: [
+        "multitasking.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "com_android_wm_shell_flags_lib",
+    aconfig_declarations: "com_android_wm_shell_flags",
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
new file mode 100644
index 0000000..d55a41f
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.wm.shell"
+
+flag {
+    name: "example_flag"
+    namespace: "multitasking"
+    description: "An Example Flag"
+    bug: "300136750"
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
index 4ee10f4..15837ad 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
@@ -15,7 +15,8 @@
   ~ limitations under the License.
   -->
 <shape android:shape="rectangle"
-       xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@android:color/white" />
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <corners android:radius="@dimen/desktop_mode_handle_menu_corner_radius" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
 </shape>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index d93e9ba..7638132 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -26,8 +26,8 @@
     <ImageButton
         android:id="@+id/caption_handle"
         android:layout_width="128dp"
-        android:layout_height="42dp"
-        android:paddingVertical="19dp"
+        android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
+        android:paddingVertical="16dp"
         android:contentDescription="@string/handle_text"
         android:src="@drawable/decor_handle_dark"
         tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
index c03d240..c2ee306 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="@dimen/desktop_mode_handle_menu_width"
     android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
@@ -35,7 +36,7 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         tools:text="Gmail"
-        android:textColor="@color/desktop_mode_caption_menu_text_color"
+        android:textColor="?androidprv:attr/materialColorOnSurface"
         android:textSize="14sp"
         android:textFontWeight="500"
         android:lineHeight="20dp"
@@ -52,6 +53,6 @@
         android:contentDescription="@string/collapse_menu_text"
         android:src="@drawable/ic_baseline_expand_more_24"
         android:rotation="180"
-        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:tint="?androidprv:attr/materialColorOnSurface"
         android:background="?android:selectableItemBackgroundBorderless"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
index cdf4937..e637671 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="@dimen/desktop_mode_handle_menu_width"
     android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
     android:orientation="vertical"
@@ -25,7 +26,7 @@
         android:contentDescription="@string/screenshot_text"
         android:text="@string/screenshot_text"
         android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
-        android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:drawableTint="?androidprv:attr/materialColorOnSurface"
         style="@style/DesktopModeHandleMenuActionButton"/>
 
     <Button
@@ -33,15 +34,14 @@
         android:contentDescription="@string/select_text"
         android:text="@string/select_text"
         android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
-        android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:drawableTint="?androidprv:attr/materialColorOnSurface"
         style="@style/DesktopModeHandleMenuActionButton"/>
-
     <Button
         android:id="@+id/close_button"
         android:contentDescription="@string/close_text"
         android:text="@string/close_text"
         android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close"
-        android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:drawableTint="?androidprv:attr/materialColorOnSurface"
         style="@style/DesktopModeHandleMenuActionButton"/>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
index 08d9149..c4b688d 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="@dimen/desktop_mode_handle_menu_width"
     android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
     android:orientation="horizontal"
@@ -26,7 +27,7 @@
         android:layout_marginEnd="4dp"
         android:contentDescription="@string/fullscreen_text"
         android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
-        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:tint="?androidprv:attr/materialColorOnSurface"
         android:layout_weight="1"
         style="@style/DesktopModeHandleMenuWindowingButton"/>
 
@@ -36,7 +37,7 @@
         android:layout_marginEnd="4dp"
         android:contentDescription="@string/split_screen_text"
         android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
-        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:tint="?androidprv:attr/materialColorOnSurface"
         android:layout_weight="1"
         style="@style/DesktopModeHandleMenuWindowingButton"/>
 
@@ -46,7 +47,7 @@
         android:layout_marginEnd="4dp"
         android:contentDescription="@string/float_button_text"
         android:src="@drawable/desktop_mode_ic_handle_menu_floating"
-        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:tint="?androidprv:attr/materialColorOnSurface"
         android:layout_weight="1"
         style="@style/DesktopModeHandleMenuWindowingButton"/>
 
@@ -55,7 +56,7 @@
         android:layout_marginStart="4dp"
         android:contentDescription="@string/desktop_text"
         android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
-        android:tint="@color/desktop_mode_caption_menu_buttons_color_active"
+        android:tint="?androidprv:attr/materialColorOnSurface"
         android:layout_weight="1"
         style="@style/DesktopModeHandleMenuWindowingButton"/>
 
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index 5f9dbdb..da8abde 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -44,6 +44,12 @@
     if a custom action is present before closing it. -->
     <integer name="config_pipForceCloseDelay">5000</integer>
 
+    <!-- Animation duration when exit starting window: fade out icon -->
+    <integer name="starting_window_app_reveal_icon_fade_out_duration">500</integer>
+
+    <!-- Animation delay when exit starting window: reveal app -->
+    <integer name="starting_window_app_reveal_anim_delay">0</integer>
+
     <!-- Animation duration when exit starting window: reveal app -->
     <integer name="starting_window_app_reveal_anim_duration">500</integer>
 
diff --git a/libs/WindowManager/Shell/res/values-watch/config.xml b/libs/WindowManager/Shell/res/values-watch/config.xml
new file mode 100644
index 0000000..03736ed
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-watch/config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- These resources are around just to allow their values to be customized
+     for watch products.  Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Animation duration when exit starting window: fade out icon -->
+    <integer name="starting_window_app_reveal_icon_fade_out_duration">50</integer>
+
+    <!-- Animation delay when exit starting window: reveal app -->
+    <integer name="starting_window_app_reveal_anim_delay">50</integer>
+
+    <!-- Animation duration when exit starting window: reveal app -->
+    <integer name="starting_window_app_reveal_anim_duration">200</integer>
+
+    <!-- Default animation type when hiding the starting window. The possible values are:
+          - 0 for radial vanish + slide up
+          - 1 for fade out -->
+    <integer name="starting_window_exit_animation_type">1</integer>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index f76a346..9bfd1b4 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -68,9 +68,6 @@
     <color name="desktop_mode_caption_maximize_button_dark">#1C1C17</color>
     <color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
     <color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
-    <color name="desktop_mode_caption_menu_text_color">#191C1D</color>
-    <color name="desktop_mode_caption_menu_buttons_color_inactive">#191C1D</color>
-    <color name="desktop_mode_caption_menu_buttons_color_active">#00677E</color>
     <color name="desktop_mode_resize_veil_light">#EFF1F2</color>
     <color name="desktop_mode_resize_veil_dark">#1C1C17</color>
     <color name="desktop_mode_maximize_menu_button">#DDDACD</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index cba86c8..7faf380 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -401,6 +401,12 @@
     <!-- Height of button (32dp)  + 2 * margin (5dp each). -->
     <dimen name="freeform_decor_caption_height">42dp</dimen>
 
+    <!-- Height of desktop mode caption for freeform tasks. -->
+    <dimen name="desktop_mode_freeform_decor_caption_height">42dp</dimen>
+
+    <!-- Height of desktop mode caption for fullscreen tasks. -->
+    <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
+
     <!-- The width of the maximize menu in desktop mode. -->
     <dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
 
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index d902fd4..468cfd5 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
     <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
         <item name="android:windowBackground">@drawable/forced_resizable_background</item>
@@ -37,7 +38,7 @@
         <item name="android:padding">16dp</item>
         <item name="android:textSize">14sp</item>
         <item name="android:textFontWeight">500</item>
-        <item name="android:textColor">@color/desktop_mode_caption_menu_text_color</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
         <item name="android:drawablePadding">16dp</item>
         <item name="android:background">?android:selectableItemBackground</item>
     </style>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 66e6930..39e3180 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -257,8 +257,16 @@
             return false;
         }
 
-        // Badged bubble image
-        Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, b.getIcon());
+        Drawable bubbleDrawable = null;
+        try {
+            // Badged bubble image
+            bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
+                    b.getIcon());
+        } catch (Exception e) {
+            // If we can't create the icon we'll default to the app icon
+            Log.w(TAG, "Exception creating icon for the bubble: " + b.getKey());
+        }
+
         if (bubbleDrawable == null) {
             // Default to app icon
             bubbleDrawable = appIcon;
@@ -268,7 +276,7 @@
                 b.isImportantConversation());
         info.badgeBitmap = badgeBitmapInfo.icon;
         // Raw badge bitmap never includes the important conversation ring
-        info.rawBadgeBitmap = b.isImportantConversation() // is this needed for bar?
+        info.rawBadgeBitmap = b.isImportantConversation()
                 ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
                 : badgeBitmapInfo.icon;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index a5000fe..f9a286e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
+
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30;
@@ -36,6 +37,8 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 
+import androidx.annotation.Nullable;
+
 import java.util.ArrayList;
 
 /**
@@ -203,6 +206,21 @@
         }
     }
 
+    /**
+     * Gets the SnapTarget corresponding to the given {@link SnapPosition}, or null if no such
+     * SnapTarget exists.
+     */
+    @Nullable
+    public SnapTarget findSnapTarget(@SnapPosition int snapPosition) {
+        for (SnapTarget t : mTargets) {
+            if (t.snapPosition == snapPosition) {
+                return t;
+            }
+        }
+
+        return null;
+    }
+
     public float calculateDismissingFraction(int position) {
         if (position < mFirstSplitTarget.position) {
             return 1f - (float) (position - getStartInset())
@@ -356,9 +374,9 @@
      * Adds a target at {@param position} but only if the area with size of {@param smallerSize}
      * meets the minimal size requirement.
      */
-    private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapTo) {
+    private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
         if (smallerSize >= mMinimalSizeResizableTask) {
-            mTargets.add(new SnapTarget(position, position, snapTo));
+            mTargets.add(new SnapTarget(position, position, snapPosition));
         }
     }
 
@@ -419,6 +437,13 @@
     }
 
     /**
+     * Finds the {@link SnapPosition} nearest to the given position.
+     */
+    public int calculateNearestSnapPosition(int currentPosition) {
+        return snap(currentPosition, /* hardDismiss */ true).snapPosition;
+    }
+
+    /**
      * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
      * if {@param increment} is negative and moves right otherwise.
      */
@@ -454,7 +479,7 @@
         /**
          * An int describing the placement of the divider in this snap target.
          */
-        public final @SnapPosition int snapTo;
+        public final @SnapPosition int snapPosition;
 
         public boolean isMiddleTarget;
 
@@ -464,15 +489,15 @@
          */
         private final float distanceMultiplier;
 
-        public SnapTarget(int position, int taskPosition, @SnapPosition int snapTo) {
-            this(position, taskPosition, snapTo, 1f);
+        public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition) {
+            this(position, taskPosition, snapPosition, 1f);
         }
 
-        public SnapTarget(int position, int taskPosition, @SnapPosition int snapTo,
+        public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition,
                 float distanceMultiplier) {
             this.position = position;
             this.taskPosition = taskPosition;
-            this.snapTo = snapTo;
+            this.snapPosition = snapPosition;
             this.distanceMultiplier = distanceMultiplier;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 4af03fd..26b5a50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
+
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
 import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
@@ -66,6 +67,7 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.InteractionJankMonitorUtils;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 
 import java.io.PrintWriter;
@@ -115,7 +117,7 @@
     @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
     private WindowContainerToken mWinToken1;
     private WindowContainerToken mWinToken2;
-    private int mDividePosition;
+    private int mDividerPosition;
     private boolean mInitialized = false;
     private boolean mFreezeDividerWindow = false;
     private int mOrientation;
@@ -267,7 +269,14 @@
     }
 
     int getDividePosition() {
-        return mDividePosition;
+        return mDividerPosition;
+    }
+
+    /**
+     * Finds the {@link SnapPosition} nearest to the current divider position.
+     */
+    public int calculateCurrentSnapPosition() {
+        return mDividerSnapAlgorithm.calculateNearestSnapPosition(mDividerPosition);
     }
 
     /**
@@ -344,16 +353,16 @@
     }
 
     private void initDividerPosition(Rect oldBounds) {
-        final float snapRatio = (float) mDividePosition
+        final float snapRatio = (float) mDividerPosition
                 / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
         // Estimate position by previous ratio.
         final float length =
                 (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
         final int estimatePosition = (int) (length * snapRatio);
         // Init divider position by estimated position using current bounds snap algorithm.
-        mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
+        mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
                 estimatePosition).position;
-        updateBounds(mDividePosition);
+        updateBounds(mDividerPosition);
     }
 
     private void updateBounds(int position) {
@@ -467,27 +476,29 @@
     }
 
     void setDividePosition(int position, boolean applyLayoutChange) {
-        mDividePosition = position;
-        updateBounds(mDividePosition);
+        mDividerPosition = position;
+        updateBounds(mDividerPosition);
         if (applyLayoutChange) {
             mSplitLayoutHandler.onLayoutSizeChanged(this);
         }
     }
 
     /** Updates divide position and split bounds base on the ratio within root bounds. */
-    public void setDivideRatio(float ratio) {
-        final int position = isLandscape()
-                ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
-                : mRootBounds.top + (int) (mRootBounds.height() * ratio);
-        final DividerSnapAlgorithm.SnapTarget snapTarget =
-                mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+    public void setDivideRatio(@SnapPosition int snapPosition) {
+        final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget(
+                snapPosition);
+
+        if (snapTarget == null) {
+            throw new IllegalArgumentException("No SnapTarget for position " + snapPosition);
+        }
+
         setDividePosition(snapTarget.position, false /* applyLayoutChange */);
     }
 
     /** Resets divider position. */
     public void resetDividerPosition() {
-        mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
-        updateBounds(mDividePosition);
+        mDividerPosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+        updateBounds(mDividerPosition);
         mWinToken1 = null;
         mWinToken2 = null;
         mWinBounds1.setEmpty();
@@ -510,7 +521,7 @@
      * target indicates dismissing split.
      */
     public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
-        switch (snapTarget.snapTo) {
+        switch (snapTarget.snapPosition) {
             case SNAP_TO_START_AND_DISMISS:
                 flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
                         () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
@@ -668,8 +679,8 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                mDividePosition = dividerPos;
-                updateBounds(mDividePosition);
+                mDividerPosition = dividerPos;
+                updateBounds(mDividerPosition);
                 finishCallback.accept(insets);
                 InteractionJankMonitorUtils.endTracing(CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER);
             }
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 c111ce6..0e6b203 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
@@ -22,10 +22,13 @@
 import android.annotation.Nullable;
 import android.app.TaskInfo;
 import android.app.TaskInfo.CameraCompatControlState;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
+import android.net.Uri;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Log;
@@ -577,7 +580,13 @@
         final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        mContext.startActivity(intent);
+        final ComponentName appComponent = taskInfo.topActivity;
+        if (appComponent != null) {
+            final Uri packageUri = Uri.parse("package:" + appComponent.getPackageName());
+            intent.setData(packageUri);
+        }
+        final UserHandle userHandle = UserHandle.of(taskInfo.userId);
+        mContext.startActivityAsUser(intent, userHandle);
     }
 
     private void removeLayouts(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index fd23d14..9f9854e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -205,6 +205,7 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
+            RecentsTransitionHandler recentsTransitionHandler,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
         if (DesktopModeStatus.isEnabled()) {
             return new DesktopModeWindowDecorViewModel(
@@ -218,6 +219,7 @@
                     syncQueue,
                     transitions,
                     desktopTasksController,
+                    recentsTransitionHandler,
                     rootTaskDisplayAreaOrganizer);
         }
         return new CaptionWindowDecorViewModel(
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 9ce0118..f8dd208 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
@@ -17,7 +17,6 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -61,6 +60,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
@@ -213,6 +213,7 @@
             "DesktopTasksController: moveToDesktop taskId=%d",
             task.taskId
         )
+        exitSplitIfApplicable(wct, task)
         // Bring other apps to front first
         bringDesktopAppsToFront(task.displayId, wct)
         addMoveToDesktopChanges(wct, task)
@@ -240,6 +241,7 @@
             taskInfo.taskId
         )
         val wct = WindowContainerTransaction()
+        exitSplitIfApplicable(wct, taskInfo)
         moveHomeTaskToFront(wct)
         addMoveToDesktopChanges(wct, taskInfo)
         wct.setBounds(taskInfo.token, startBounds)
@@ -319,7 +321,7 @@
             task.taskId
         )
         val wct = WindowContainerTransaction()
-        wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
+        wct.setWindowingMode(task.token, WINDOWING_MODE_MULTI_WINDOW)
         wct.setBounds(task.token, Rect())
         wct.setDensityDpi(task.token, getDefaultDensityDpi())
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -329,6 +331,13 @@
         }
     }
 
+    private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
+        if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+            splitScreenController.prepareExitSplitScreen(wct,
+                splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+        }
+    }
+
     /**
      * The second part of the animated move to desktop transition, called after
      * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 069066e..2616b8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -34,4 +34,10 @@
     default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
             Consumer<List<GroupedRecentTaskInfo>> callback) {
     }
+
+    /**
+     * Adds the listener to be notified of whether the recent task animation is running.
+     */
+    default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) {
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 94e1b33..ccc3438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -428,6 +428,21 @@
                 executor.execute(() -> callback.accept(tasks));
             });
         }
+
+        @Override
+        public void addAnimationStateListener(Executor executor, Consumer<Boolean> listener) {
+            mMainExecutor.execute(() -> {
+                if (mTransitionHandler == null) {
+                    return;
+                }
+                mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+                    @Override
+                    public void onAnimationStateChanged(boolean running) {
+                        executor.execute(() -> listener.accept(running));
+                    }
+                });
+            });
+        }
     }
 
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index dc6dc79..ead2f9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -76,6 +76,7 @@
     private final RecentTasksController mRecentTasksController;
     private IApplicationThread mAnimApp = null;
     private final ArrayList<RecentsController> mControllers = new ArrayList<>();
+    private final ArrayList<RecentsTransitionStateListener> mStateListeners = new ArrayList<>();
 
     /**
      * List of other handlers which might need to mix recents with other things. These are checked
@@ -106,6 +107,11 @@
         mMixers.remove(mixer);
     }
 
+    /** Adds the callback for receiving the state change of transition. */
+    public void addTransitionStateListener(RecentsTransitionStateListener listener) {
+        mStateListeners.add(listener);
+    }
+
     @VisibleForTesting
     public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
             IApplicationThread appThread, IRecentsAnimationRunner listener) {
@@ -128,6 +134,9 @@
         }
         final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
                 mixedHandler == null ? this : mixedHandler);
+        for (int i = 0; i < mStateListeners.size(); i++) {
+            mStateListeners.get(i).onTransitionStarted(transition);
+        }
         if (mixer != null) {
             mixer.setRecentsTransition(transition);
         }
@@ -171,13 +180,14 @@
             return false;
         }
         final RecentsController controller = mControllers.get(controllerIdx);
-        Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
+        final IApplicationThread animApp = mAnimApp;
         mAnimApp = null;
         if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                     "RecentsTransitionHandler.startAnimation: failed to start animation");
             return false;
         }
+        Transitions.setRunningRemoteTransitionDelegate(animApp);
         return true;
     }
 
@@ -225,6 +235,13 @@
         private ArrayList<TaskState> mPausingTasks = null;
 
         /**
+         * List of tasks were pausing but closed in a subsequent merged transition. If a
+         * closing task is reopened, the leash is not initially hidden since it is already
+         * visible.
+         */
+        private ArrayList<TaskState> mClosingTasks = null;
+
+        /**
          * List of tasks that we are switching to. Upon finish, these will remain visible and
          * on top.
          */
@@ -375,11 +392,15 @@
             }
             mFinishTransaction = null;
             mPausingTasks = null;
+            mClosingTasks = null;
             mOpeningTasks = null;
             mInfo = null;
             mTransition = null;
             mPendingPauseSnapshotsForCancel = null;
             mControllers.remove(this);
+            for (int i = 0; i < mStateListeners.size(); i++) {
+                mStateListeners.get(i).onAnimationStateChanged(false);
+            }
         }
 
         boolean start(TransitionInfo info, SurfaceControl.Transaction t,
@@ -419,6 +440,7 @@
             mFinishCB = finishCB;
             mFinishTransaction = finishT;
             mPausingTasks = new ArrayList<>();
+            mClosingTasks = new ArrayList<>();
             mOpeningTasks = new ArrayList<>();
             mLeashMap = new ArrayMap<>();
             mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
@@ -518,6 +540,9 @@
                         apps.toArray(new RemoteAnimationTarget[apps.size()]),
                         wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
                         new Rect(0, 0, 0, 0), new Rect(), b);
+                for (int i = 0; i < mStateListeners.size(); i++) {
+                    mStateListeners.get(i).onAnimationStateChanged(true);
+                }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error starting recents animation", e);
                 cancel("onAnimationStart() failed");
@@ -670,7 +695,10 @@
                     final TransitionInfo.Change change = closingTasks.get(i);
                     final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
                     if (pausingIdx >= 0) {
-                        mPausingTasks.remove(pausingIdx);
+                        // We are closing the pausing task, but it is still visible and can be
+                        // restart by another transition prior to this transition finishing
+                        final TaskState closingTask = mPausingTasks.remove(pausingIdx);
+                        mClosingTasks.add(closingTask);
                         didMergeThings = true;
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                                 "  closing pausing taskId=%d", change.getTaskInfo().taskId);
@@ -706,7 +734,12 @@
                 for (int i = 0; i < openingTasks.size(); ++i) {
                     final TransitionInfo.Change change = openingTasks.get(i);
                     final boolean isLeaf = openingTaskIsLeafs.get(i) == 1;
-                    int pausingIdx = TaskState.indexOf(mPausingTasks, change);
+                    final int closingIdx = TaskState.indexOf(mClosingTasks, change);
+                    if (closingIdx >= 0) {
+                        // Remove opening tasks from closing set
+                        mClosingTasks.remove(closingIdx);
+                    }
+                    final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
                     if (pausingIdx >= 0) {
                         // Something is showing/opening a previously-pausing app.
                         if (isLeaf) {
@@ -729,12 +762,23 @@
                         appearedTargets[nextTargetIdx++] = target;
                         // reparent into the original `mInfo` since that's where we are animating.
                         final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+                        final boolean wasClosing = closingIdx >= 0;
                         t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
                         t.setLayer(target.leash, layer);
-                        // Hide the animation leash, let listener show it.
-                        t.hide(target.leash);
+                        if (wasClosing) {
+                            // App was previously visible and is closing
+                            t.show(target.leash);
+                            t.setAlpha(target.leash, 1f);
+                            // Also override the task alpha as it was set earlier when dispatching
+                            // the transition and setting up the leash to hide the
+                            t.setAlpha(change.getLeash(), 1f);
+                        } else {
+                            // Hide the animation leash, let the listener show it
+                            t.hide(target.leash);
+                        }
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
-                                "  opening new leaf taskId=%d", target.taskId);
+                                "  opening new leaf taskId=%d wasClosing=%b",
+                                target.taskId, wasClosing);
                         mOpeningTasks.add(new TaskState(change, target.leash));
                     } else {
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
new file mode 100644
index 0000000..e8733eb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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.recents;
+
+import android.os.IBinder;
+
+/** The listener for the events from {@link RecentsTransitionHandler}. */
+public interface RecentsTransitionStateListener {
+
+    /** Notifies whether the recents animation is running. */
+    default void onAnimationStateChanged(boolean running) {
+    }
+
+    /** Notifies that a recents shell transition has started. */
+    default void onTransitionStarted(IBinder transition) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 14304a3..253acc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -91,42 +91,42 @@
      * Starts tasks simultaneously in one transition.
      */
     oneway void startTasks(int taskId1, in Bundle options1, int taskId2, in Bundle options2,
-            int splitPosition, float splitRatio, in RemoteTransition remoteTransition,
+            int splitPosition, int snapPosition, in RemoteTransition remoteTransition,
             in InstanceId instanceId) = 10;
 
     /**
      * Starts a pair of intent and task in one transition.
      */
     oneway void startIntentAndTask(in PendingIntent pendingIntent, int userId1, in Bundle options1,
-            int taskId, in Bundle options2, int sidePosition, float splitRatio,
+            int taskId, in Bundle options2, int sidePosition, int snapPosition,
             in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
 
     /**
      * Starts a pair of shortcut and task in one transition.
      */
     oneway void startShortcutAndTask(in ShortcutInfo shortcutInfo, in Bundle options1, int taskId,
-            in Bundle options2, int splitPosition, float splitRatio,
+            in Bundle options2, int splitPosition, int snapPosition,
             in RemoteTransition remoteTransition, in InstanceId instanceId) = 17;
 
     /**
      * Version of startTasks using legacy transition system.
      */
     oneway void startTasksWithLegacyTransition(int taskId1, in Bundle options1, int taskId2,
-            in Bundle options2, int splitPosition, float splitRatio,
+            in Bundle options2, int splitPosition, int snapPosition,
             in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11;
 
     /**
      * Starts a pair of intent and task using legacy transition system.
      */
     oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, int userId1,
-            in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+            in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition,
             in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12;
 
     /**
      * Starts a pair of shortcut and task using legacy transition system.
      */
     oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo,
-            in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+            in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition,
             in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15;
 
     /**
@@ -135,7 +135,7 @@
     oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1, int userId1,
             in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
             int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
-            float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
+            int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
 
     /**
      * Start a pair of intents in one transition.
@@ -143,7 +143,7 @@
     oneway void startIntents(in PendingIntent pendingIntent1, int userId1,
             in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
             int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
-            float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
+            int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
 
     /**
      * Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 991b699..ccffa02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,7 +23,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -86,6 +85,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -131,6 +131,7 @@
     public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
     public static final int EXIT_REASON_RECREATE_SPLIT = 10;
     public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
+    public static final int EXIT_REASON_ENTER_DESKTOP = 12;
     @IntDef(value = {
             EXIT_REASON_UNKNOWN,
             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -144,6 +145,7 @@
             EXIT_REASON_CHILD_TASK_ENTER_PIP,
             EXIT_REASON_RECREATE_SPLIT,
             EXIT_REASON_FULLSCREEN_SHORTCUT,
+            EXIT_REASON_ENTER_DESKTOP
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ExitReason{}
@@ -599,8 +601,8 @@
 
     void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (options1 == null) options1 = new Bundle();
         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
 
@@ -624,13 +626,14 @@
         }
 
         mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
-                activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
+                activityOptions.toBundle(), taskId, options2, splitPosition, snapPosition, adapter,
                 instanceId);
     }
 
     void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
-            float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+            @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+            InstanceId instanceId) {
         if (options1 == null) options1 = new Bundle();
         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
         final String packageName1 = shortcutInfo.getPackage();
@@ -657,7 +660,7 @@
             }
         }
         mStageCoordinator.startShortcutAndTask(shortcutInfo, activityOptions.toBundle(), taskId,
-                options2, splitPosition, splitRatio, remoteTransition, instanceId);
+                options2, splitPosition, snapPosition, remoteTransition, instanceId);
     }
 
     /**
@@ -672,8 +675,8 @@
 
     private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         Intent fillInIntent = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
@@ -694,12 +697,12 @@
             }
         }
         mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
-                options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+                options1, taskId, options2, splitPosition, snapPosition, adapter, instanceId);
     }
 
     private void startIntentAndTask(PendingIntent pendingIntent, int userId1,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio,
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         Intent fillInIntent = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
@@ -726,14 +729,14 @@
             }
         }
         mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
-                options2, splitPosition, splitRatio, remoteTransition, instanceId);
+                options2, splitPosition, snapPosition, remoteTransition, instanceId);
     }
 
     private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
             PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
-            @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
-            RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable Bundle options2, @SplitPosition int splitPosition,
+            @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         Intent fillInIntent1 = null;
         Intent fillInIntent2 = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
@@ -757,14 +760,15 @@
         }
         mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1,
                 shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
-                splitPosition, splitRatio, adapter, instanceId);
+                splitPosition, snapPosition, adapter, instanceId);
     }
 
     private void startIntents(PendingIntent pendingIntent1, int userId1,
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
             PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
-            @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
-            @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+            @Nullable Bundle options2, @SplitPosition int splitPosition,
+            @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+            InstanceId instanceId) {
         Intent fillInIntent1 = null;
         Intent fillInIntent2 = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
@@ -799,7 +803,7 @@
         }
         mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
                 activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
-                activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+                activityOptions2.toBundle(), splitPosition, snapPosition, remoteTransition,
                 instanceId);
     }
 
@@ -1011,6 +1015,8 @@
                 return "CHILD_TASK_ENTER_PIP";
             case EXIT_REASON_RECREATE_SPLIT:
                 return "RECREATE_SPLIT";
+            case EXIT_REASON_ENTER_DESKTOP:
+                return "ENTER_DESKTOP";
             default:
                 return "unknown reason, reason int = " + exitReason;
         }
@@ -1219,78 +1225,82 @@
         @Override
         public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
                 int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
-                float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+                @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startTasks",
                     (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
-                            taskId1, options1, taskId2, options2, splitPosition,
-                            splitRatio, adapter, instanceId));
+                            taskId1, options1, taskId2, options2, splitPosition, snapPosition,
+                            adapter, instanceId));
         }
 
         @Override
         public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
-                Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
-                RemoteAnimationAdapter adapter, InstanceId instanceId) {
+                Bundle options1, int taskId, Bundle options2, int splitPosition,
+                @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController,
                     "startIntentAndTaskWithLegacyTransition", (controller) ->
                             controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
-                                    userId1, options1, taskId, options2, splitPosition, splitRatio,
-                                    adapter, instanceId));
+                                    userId1, options1, taskId, options2, splitPosition,
+                                    snapPosition, adapter, instanceId));
         }
 
         @Override
         public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
                 @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-                @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-                InstanceId instanceId) {
+                @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+                RemoteAnimationAdapter adapter, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController,
                     "startShortcutAndTaskWithLegacyTransition", (controller) ->
                             controller.startShortcutAndTaskWithLegacyTransition(
                                     shortcutInfo, options1, taskId, options2, splitPosition,
-                                    splitRatio, adapter, instanceId));
+                                    snapPosition, adapter, instanceId));
         }
 
         @Override
         public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
-                @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
-                @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+                @Nullable Bundle options2, @SplitPosition int splitPosition,
+                @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startTasks",
                     (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
-                            taskId2, options2, splitPosition, splitRatio, remoteTransition,
+                            taskId2, options2, splitPosition, snapPosition, remoteTransition,
                             instanceId));
         }
 
         @Override
         public void startIntentAndTask(PendingIntent pendingIntent, int userId1,
                 @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-                @SplitPosition int splitPosition, float splitRatio,
+                @SplitPosition int splitPosition, @SnapPosition int snapPosition,
                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
                     (controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1,
-                            taskId, options2, splitPosition, splitRatio, remoteTransition,
+                            taskId, options2, splitPosition, snapPosition, remoteTransition,
                             instanceId));
         }
 
         @Override
         public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
                 int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
-                float splitRatio, @Nullable RemoteTransition remoteTransition,
+                @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
                 InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
                     (controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId,
-                            options2, splitPosition, splitRatio, remoteTransition, instanceId));
+                            options2, splitPosition, snapPosition, remoteTransition, instanceId));
         }
 
         @Override
         public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
                 @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
                 PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
-                @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
-                RemoteAnimationAdapter adapter, InstanceId instanceId) {
+                @Nullable Bundle options2, @SplitPosition int splitPosition,
+                @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
                     (controller) ->
                         controller.startIntentsWithLegacyTransition(pendingIntent1, userId1,
                                 shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2,
-                                options2, splitPosition, splitRatio, adapter, instanceId)
+                                options2, splitPosition, snapPosition, adapter, instanceId)
                     );
         }
 
@@ -1298,13 +1308,14 @@
         public void startIntents(PendingIntent pendingIntent1, int userId1,
                 @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
                 PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
-                @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
-                @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+                @Nullable Bundle options2, @SplitPosition int splitPosition,
+                @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntents",
                     (controller) ->
                             controller.startIntents(pendingIntent1, userId1, shortcutInfo1,
                                     options1, pendingIntent2, userId2, shortcutInfo2, options2,
-                                    splitPosition, splitRatio, remoteTransition, instanceId)
+                                    splitPosition, snapPosition, remoteTransition, instanceId)
             );
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 5483fa5..f4ab226 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,6 +25,7 @@
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -42,6 +43,7 @@
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -192,6 +194,8 @@
                 return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
             case EXIT_REASON_FULLSCREEN_SHORTCUT:
                 return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+            case EXIT_REASON_ENTER_DESKTOP:
+                return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
             case EXIT_REASON_UNKNOWN:
                 // Fall through
             default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 94fa485..003273f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -128,6 +128,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.common.split.SplitWindowManager;
@@ -631,8 +632,8 @@
     }
 
     /** Starts 2 tasks in one transition. */
-    void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
-            @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
+    void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId2 == INVALID_TASK_ID) {
@@ -654,13 +655,13 @@
         addActivityOptions(options1, mSideStage);
         wct.startTask(taskId1, options1);
 
-        startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId);
+        startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
     }
 
     /** Start an intent and a task to a split pair in one transition. */
     void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio,
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId == INVALID_TASK_ID) {
@@ -676,13 +677,14 @@
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
 
-        startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId);
+        startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
     }
 
     /** Starts a shortcut and a task to a split pair in one transition. */
     void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
-            float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+            @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+            InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId == INVALID_TASK_ID) {
             options1 = options1 != null ? options1 : new Bundle();
@@ -697,7 +699,7 @@
         addActivityOptions(options1, mSideStage);
         wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
 
-        startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId);
+        startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
     }
 
     /**
@@ -708,14 +710,14 @@
      *                   {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
      */
     private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
-            @Nullable Bundle mainOptions, float splitRatio,
+            @Nullable Bundle mainOptions, @SnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         if (!mMainStage.isActive()) {
             // Build a request WCT that will launch both apps such that task 0 is on the main stage
             // while task 1 is on the side stage.
             mMainStage.activate(wct, false /* reparent */);
         }
-        mSplitLayout.setDivideRatio(splitRatio);
+        mSplitLayout.setDivideRatio(snapPosition);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
         setRootForceTranslucent(false, wct);
@@ -740,7 +742,7 @@
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
             PendingIntent pendingIntent2, Intent fillInIntent2,
             @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio,
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (pendingIntent2 == null) {
@@ -762,7 +764,7 @@
         }
 
         setSideStagePosition(splitPosition, wct);
-        mSplitLayout.setDivideRatio(splitRatio);
+        mSplitLayout.setDivideRatio(snapPosition);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
         setRootForceTranslucent(false, wct);
@@ -790,7 +792,7 @@
     /** Starts a pair of tasks using legacy transition. */
     void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
             int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
-            float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (taskId2 == INVALID_TASK_ID) {
@@ -811,7 +813,7 @@
         addActivityOptions(options1, mSideStage);
         wct.startTask(taskId1, options1);
         mSplitRequest = new SplitRequest(taskId1, taskId2, splitPosition);
-        startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
+        startWithLegacyTransition(wct, taskId2, options2, splitPosition, snapPosition, adapter,
                 instanceId);
     }
 
@@ -820,8 +822,8 @@
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
             @Nullable PendingIntent pendingIntent2, Intent fillInIntent2,
             @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (pendingIntent2 == null) {
@@ -840,13 +842,13 @@
                     pendingIntent2 != null ? pendingIntent2.getIntent() : null, splitPosition);
         }
         startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
-                splitPosition, splitRatio, adapter, instanceId);
+                splitPosition, snapPosition, adapter, instanceId);
     }
 
     void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (taskId == INVALID_TASK_ID) {
@@ -859,15 +861,15 @@
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
         mSplitRequest = new SplitRequest(taskId, pendingIntent.getIntent(), splitPosition);
-        startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
+        startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter,
                 instanceId);
     }
 
     /** Starts a pair of shortcut and task using legacy transition. */
     void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
-            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (taskId == INVALID_TASK_ID) {
@@ -878,7 +880,7 @@
 
         addActivityOptions(options1, mSideStage);
         wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
-        startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
+        startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter,
                 instanceId);
     }
 
@@ -928,18 +930,19 @@
     private void startWithLegacyTransition(WindowContainerTransaction wct,
             @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
             @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions,
-            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int sidePosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         startWithLegacyTransition(wct, INVALID_TASK_ID, mainPendingIntent, mainFillInIntent,
-                mainShortcutInfo, mainOptions, sidePosition, splitRatio, adapter, instanceId);
+                mainShortcutInfo, mainOptions, sidePosition, snapPosition, adapter, instanceId);
     }
 
     private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
-            @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable Bundle mainOptions, @SplitPosition int sidePosition,
+            @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         startWithLegacyTransition(wct, mainTaskId, null /* mainPendingIntent */,
                 null /* mainFillInIntent */, null /* mainShortcutInfo */, mainOptions, sidePosition,
-                splitRatio, adapter, instanceId);
+                snapPosition, adapter, instanceId);
     }
 
     /**
@@ -950,15 +953,15 @@
     private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
             @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
             @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle options,
-            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
+            @SplitPosition int sidePosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (!isSplitScreenVisible()) {
             exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
         }
 
         // Init divider first to make divider leash for remote animation target.
         mSplitLayout.init();
-        mSplitLayout.setDivideRatio(splitRatio);
+        mSplitLayout.setDivideRatio(snapPosition);
 
         // Apply surface bounds before animation start.
         SurfaceControl.Transaction startT = mTransactionPool.acquire();
@@ -1761,7 +1764,7 @@
                 rightBottomTaskId = sideStageTopTaskId;
             }
             SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
-                    leftTopTaskId, rightBottomTaskId);
+                    leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
             if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
                 // Update the pair for the top tasks
                 recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index cb76044..edb5aba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -20,7 +20,6 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM;
 
 import android.animation.Animator;
-import android.annotation.IntDef;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.Slog;
@@ -47,7 +46,7 @@
     private final int mIconFadeOutDuration;
     private final int mAppRevealDelay;
     private final int mAppRevealDuration;
-    @ExitAnimationType
+    @SplashScreenExitAnimationUtils.ExitAnimationType
     private final int mAnimationType;
     private final int mAnimationDuration;
     private final float mIconStartAlpha;
@@ -58,24 +57,6 @@
 
     private Runnable mFinishCallback;
 
-    /**
-    * This splash screen exit animation type uses a radial vanish to hide
-    * the starting window and slides up the main window content.
-    */
-    private static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
-
-    /**
-    * This splash screen exit animation type fades out the starting window
-    * to reveal the main window content.
-    */
-    private static final int TYPE_FADE_OUT = 1;
-
-    @IntDef(prefix = { "TYPE_" }, value = {
-        TYPE_RADIAL_VANISH_SLIDE_UP,
-        TYPE_FADE_OUT,
-    })
-    private @interface ExitAnimationType {}
-
     SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
             Rect frame, int mainWindowShiftLength, TransactionPool pool, Runnable handleFinish,
             float roundedCornerRadius) {
@@ -121,15 +102,10 @@
     }
 
     void startAnimations() {
-        if (mAnimationType == TYPE_FADE_OUT) {
-            SplashScreenExitAnimationUtils.startFadeOutAnimation(mSplashScreenView,
-                    mAnimationDuration, this);
-        } else {
-            SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface,
-                    mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration,
-                    mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay,
-                    mAppRevealDuration, this, mRoundedCornerRadius);
-        }
+        SplashScreenExitAnimationUtils.startAnimations(mAnimationType, mSplashScreenView,
+                mFirstWindowSurface, mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame,
+                mAnimationDuration, mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha,
+                mAppRevealDelay, mAppRevealDuration, this, mRoundedCornerRadius);
     }
 
     private void reset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
index 64f09a7..e86b62d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
@@ -20,6 +20,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.IntDef;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -54,6 +55,7 @@
 public class SplashScreenExitAnimationUtils {
     private static final boolean DEBUG_EXIT_ANIMATION = false;
     private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+    private static final boolean DEBUG_EXIT_FADE_ANIMATION = false;
     private static final String TAG = "SplashScreenExitAnimationUtils";
 
     private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
@@ -62,19 +64,47 @@
     private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
 
     /**
+     * This splash screen exit animation type uses a radial vanish to hide
+     * the starting window and slides up the main window content.
+     * @hide
+     */
+    public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
+
+    /**
+     * This splash screen exit animation type fades out the starting window
+     * to reveal the main window content.
+     * @hide
+     */
+    public static final int TYPE_FADE_OUT = 1;
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_RADIAL_VANISH_SLIDE_UP,
+            TYPE_FADE_OUT,
+    })
+    public @interface ExitAnimationType {}
+
+    /**
      * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
      * window with rounded corner radius.
      */
-    static void startAnimations(ViewGroup splashScreenView,
-            SurfaceControl firstWindowSurface, int mainWindowShiftLength,
-            TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
-            int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
-            int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
-            float roundedCornerRadius) {
-        ValueAnimator animator = createRadialVanishSlideUpAnimator(splashScreenView,
-                firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
-                animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
-                appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+    static void startAnimations(@ExitAnimationType int animationType,
+            ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
+            int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
+            int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+            float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+            Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
+        ValueAnimator animator;
+        if (animationType == TYPE_FADE_OUT) {
+            animator = createFadeOutAnimation(splashScreenView, animationDuration,
+                    iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay,
+                    appRevealDuration, animatorListener);
+        } else {
+            animator = createRadialVanishSlideUpAnimator(splashScreenView,
+                    firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+                    animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+                    appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+        }
         animator.start();
     }
 
@@ -88,10 +118,11 @@
             TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
             int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
             int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
-        startAnimations(splashScreenView, firstWindowSurface, mainWindowShiftLength,
-                transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
-                iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
-                animatorListener, 0f /* roundedCornerRadius */);
+        // Start the default 'reveal' animation.
+        startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView,
+                firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+                animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+                appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */);
     }
 
     /**
@@ -209,18 +240,57 @@
         return nightMode == Configuration.UI_MODE_NIGHT_YES;
     }
 
-    static void startFadeOutAnimation(ViewGroup splashScreenView,
-            int animationDuration, Animator.AnimatorListener animatorListener) {
-        final ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
+    private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView,
+            int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+            float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+            Animator.AnimatorListener animatorListener) {
+
+        if (DEBUG_EXIT_FADE_ANIMATION) {
+            splashScreenView.setBackgroundColor(Color.BLUE);
+        }
+
+        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
         animator.setDuration(animationDuration);
         animator.setInterpolator(Interpolators.LINEAR);
         animator.addUpdateListener(animation -> {
-            splashScreenView.setAlpha((float) animation.getAnimatedValue());
+
+            float linearProgress = (float) animation.getAnimatedValue();
+
+            // Icon fade out progress (always starts immediately)
+            final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress(
+                            linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+            View iconView = null;
+            View brandingView = null;
+
+            if (splashScreenView instanceof SplashScreenView) {
+                iconView = ((SplashScreenView) splashScreenView).getIconView();
+                brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+            }
+            if (iconView != null) {
+                iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress));
+            }
+            if (brandingView != null) {
+                brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress));
+            }
+
+            // Splash screen fade out progress (possibly delayed)
+            final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation(
+                    getProgress(linearProgress, appRevealDelay,
+                    appRevealDuration, animationDuration));
+
+            splashScreenView.setAlpha(1f - splashFadeProgress);
+
+            if (DEBUG_EXIT_FADE_ANIMATION) {
+                Slog.d(TAG, "progress -> animation: " + linearProgress
+                        + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a")
+                        + "\t splash alpha: " + splashScreenView.getAlpha()
+                );
+            }
         });
         if (animatorListener != null) {
             animator.addListener(animatorListener);
         }
-        animator.start();
+        return animator;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 72fc8686..6ea6516 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -30,7 +30,7 @@
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
 
 import android.window.StartingWindowInfo;
@@ -52,8 +52,7 @@
         final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
         final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
         final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
-        final boolean isSolidColorSplashScreen =
-                (parameter & TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN) != 0;
+        final boolean allowIcon = (parameter & TYPE_PARAMETER_ALLOW_ICON) != 0;
         final boolean legacySplashScreen =
                 ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
         final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
@@ -67,13 +66,13 @@
                         + "processRunning=%b, "
                         + "allowTaskSnapshot=%b, "
                         + "activityCreated=%b, "
-                        + "isSolidColorSplashScreen=%b, "
+                        + "allowIcon=%b, "
                         + "legacySplashScreen=%b, "
                         + "activityDrawn=%b, "
                         + "windowless=%b, "
                         + "topIsHome=%b",
                 newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
-                isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
+                allowIcon, legacySplashScreen, activityDrawn, windowlessSurface,
                 topIsHome);
 
         if (windowlessSurface) {
@@ -81,7 +80,7 @@
         }
         if (!topIsHome) {
             if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
-                return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+                return getSplashscreenType(allowIcon, legacySplashScreen);
             }
         }
 
@@ -95,18 +94,18 @@
                 }
             }
             if (!activityDrawn && !topIsHome) {
-                return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+                return getSplashscreenType(allowIcon, legacySplashScreen);
             }
         }
         return STARTING_WINDOW_TYPE_NONE;
     }
 
-    private static int getSplashscreenType(boolean solidColorSplashScreen,
-            boolean legacySplashScreen) {
-        return solidColorSplashScreen
-                ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
-                : legacySplashScreen
-                        ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+    private static int getSplashscreenType(boolean allowIcon, boolean legacySplashScreen) {
+        if (allowIcon) {
+            return legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
                         : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else {
+            return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index adae21b..93d7636 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -54,12 +54,13 @@
     private static final String TAG = TaskViewTaskController.class.getSimpleName();
 
     private final CloseGuard mGuard = new CloseGuard();
-
+    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    /** Used to inset the activity content to allow space for a caption bar. */
+    private final Binder mCaptionInsetsOwner = new Binder();
     private final ShellTaskOrganizer mTaskOrganizer;
     private final Executor mShellExecutor;
     private final SyncTransactionQueue mSyncQueue;
     private final TaskViewTransitions mTaskViewTransitions;
-    private TaskViewBase mTaskViewBase;
     private final Context mContext;
 
     /**
@@ -70,21 +71,19 @@
      * in this situation to allow us to notify listeners correctly if the task failed to open.
      */
     private ActivityManager.RunningTaskInfo mPendingInfo;
-    /* Indicates that the task we attempted to launch in the task view failed to launch. */
-    private boolean mTaskNotFound;
+    private TaskViewBase mTaskViewBase;
     protected ActivityManager.RunningTaskInfo mTaskInfo;
     private WindowContainerToken mTaskToken;
     private SurfaceControl mTaskLeash;
-    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    /* Indicates that the task we attempted to launch in the task view failed to launch. */
+    private boolean mTaskNotFound;
     private boolean mSurfaceCreated;
     private SurfaceControl mSurfaceControl;
     private boolean mIsInitialized;
     private boolean mNotifiedForInitialized;
+    private boolean mHideTaskWithSurface = true;
     private TaskView.Listener mListener;
     private Executor mListenerExecutor;
-
-    /** Used to inset the activity content to allow space for a caption bar. */
-    private final Binder mCaptionInsetsOwner = new Binder();
     private Rect mCaptionInsets;
 
     public TaskViewTaskController(Context context, ShellTaskOrganizer organizer,
@@ -102,6 +101,19 @@
         mGuard.open("release");
     }
 
+    /**
+     * Specifies if the task should be hidden when the surface is destroyed.
+     * <p>This is {@code true} by default.
+     *
+     * @param hideTaskWithSurface {@code false} if task needs to remain visible even when the
+     *                            surface is destroyed, {@code true} otherwise.
+     */
+    public void setHideTaskWithSurface(boolean hideTaskWithSurface) {
+        // TODO(b/299535374): Remove mHideTaskWithSurface once the taskviews with launch root tasks
+        // are moved to a window in SystemUI in auto.
+        mHideTaskWithSurface = hideTaskWithSurface;
+    }
+
     SurfaceControl getSurfaceControl() {
         return mSurfaceControl;
     }
@@ -257,9 +269,17 @@
         mTaskNotFound = false;
     }
 
+    /** This method shouldn't be called when shell transitions are enabled. */
     private void updateTaskVisibility() {
+        boolean visible = mSurfaceCreated;
+        if (!visible && !mHideTaskWithSurface) {
+            return;
+        }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+        wct.setHidden(mTaskToken, !visible /* hidden */);
+        if (!visible) {
+            wct.reorder(mTaskToken, false /* onTop */);
+        }
         mSyncQueue.queue(wct);
         if (mListener == null) {
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index bbf67a6..a90edf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -137,7 +137,6 @@
                 });
             }
         };
-        Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
         try {
             // If the remote is actually in the same process, then make a copy of parameters since
             // remote impls assume that they have to clean-up native references.
@@ -149,6 +148,7 @@
             remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
             // assume that remote will apply the start transaction.
             startTransaction.clear();
+            Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
         } catch (RemoteException e) {
             Log.e(Transitions.TAG, "Error running remote transition.", e);
             unhandleDeath(remote.asBinder(), finishCallback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
index 0edcff4..a68b41d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
+
 import java.util.Objects;
 
 /**
@@ -37,6 +39,7 @@
     public final float leftTaskPercent;
     public final float dividerWidthPercent;
     public final float dividerHeightPercent;
+    public final @SnapPosition int snapPosition;
     /**
      * If {@code true}, that means at the time of creation of this object, the
      * split-screened apps were vertically stacked. This is useful in scenarios like
@@ -47,12 +50,13 @@
     public final int leftTopTaskId;
     public final int rightBottomTaskId;
 
-    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
-            int leftTopTaskId, int rightBottomTaskId) {
+    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+            int rightBottomTaskId, @SnapPosition int snapPosition) {
         this.leftTopBounds = leftTopBounds;
         this.rightBottomBounds = rightBottomBounds;
         this.leftTopTaskId = leftTopTaskId;
         this.rightBottomTaskId = rightBottomTaskId;
+        this.snapPosition = snapPosition;
 
         if (rightBottomBounds.top > leftTopBounds.top) {
             // vertical apps, horizontal divider
@@ -83,8 +87,9 @@
         appsStackedVertically = parcel.readBoolean();
         leftTopTaskId = parcel.readInt();
         rightBottomTaskId = parcel.readInt();
-        dividerWidthPercent = parcel.readInt();
-        dividerHeightPercent = parcel.readInt();
+        dividerWidthPercent = parcel.readFloat();
+        dividerHeightPercent = parcel.readFloat();
+        snapPosition = parcel.readInt();
     }
 
     @Override
@@ -99,6 +104,7 @@
         parcel.writeInt(rightBottomTaskId);
         parcel.writeFloat(dividerWidthPercent);
         parcel.writeFloat(dividerHeightPercent);
+        parcel.writeInt(snapPosition);
     }
 
     @Override
@@ -129,7 +135,8 @@
         return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
                 + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId +  "\n"
                 + "Divider: " + visualDividerBounds + "\n"
-                + "AppsVertical? " + appsStackedVertically;
+                + "AppsVertical? " + appsStackedVertically + "\n"
+                + "snapPosition: " + snapPosition;
     }
 
     public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index c189731..82fc0f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
@@ -114,7 +115,7 @@
         mRelayoutParams.reset();
         mRelayoutParams.mRunningTaskInfo = taskInfo;
         mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
-        mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+        mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
         mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
 
@@ -227,7 +228,7 @@
     }
 
     @Override
-    int getCaptionHeightId() {
+    int getCaptionHeightId(@WindowingMode int windowingMode) {
         return R.dimen.freeform_decor_caption_height;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 026d0cd..afa2754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
@@ -68,10 +69,13 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.recents.RecentsTransitionStateListener;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -101,6 +105,7 @@
     private final DisplayController mDisplayController;
     private final SyncTransactionQueue mSyncQueue;
     private final Optional<DesktopTasksController> mDesktopTasksController;
+    private final RecentsTransitionHandler mRecentsTransitionHandler;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -120,7 +125,8 @@
 
     private MoveToDesktopAnimator mMoveToDesktopAnimator;
     private final Rect mDragToDesktopAnimationStartBounds = new Rect();
-    private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
+    private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
+            new DesktopModeKeyguardChangeListener();
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
 
     public DesktopModeWindowDecorViewModel(
@@ -134,6 +140,7 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
+            RecentsTransitionHandler recentsTransitionHandler,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
     ) {
         this(
@@ -147,10 +154,10 @@
                 syncQueue,
                 transitions,
                 desktopTasksController,
+                recentsTransitionHandler,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
-                new DesktopModeKeyguardChangeListener(),
                 rootTaskDisplayAreaOrganizer);
     }
 
@@ -166,10 +173,10 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
+            RecentsTransitionHandler recentsTransitionHandler,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
-            DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
         mContext = context;
         mMainHandler = mainHandler;
@@ -181,11 +188,11 @@
         mSyncQueue = syncQueue;
         mTransitions = transitions;
         mDesktopTasksController = desktopTasksController;
+        mRecentsTransitionHandler = recentsTransitionHandler;
 
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
-        mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
 
         shellInit.addInitCallback(this::onInit, this);
@@ -193,6 +200,12 @@
 
     private void onInit() {
         mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
+        mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+            @Override
+            public void onTransitionStarted(IBinder transition) {
+                onRecentsTransitionStarted(transition);
+            }
+        });
     }
 
     @Override
@@ -318,6 +331,16 @@
         }
     }
 
+    private void onRecentsTransitionStarted(IBinder transition) {
+        // Block relayout on window decorations originating from #onTaskInfoChanges until the
+        // animation completes to avoid interfering with the transition animation.
+        for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
+            final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+            decor.incrementRelayoutBlock();
+            decor.addTransitionPausingRelayout(transition);
+        }
+    }
+
     private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
             implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
             DragDetector.MotionEventHandler{
@@ -348,13 +371,8 @@
             final int id = v.getId();
             if (id == R.id.close_window || id == R.id.close_button) {
                 mTaskOperations.closeTask(mTaskToken);
-                if (mSplitScreenController != null
-                        && mSplitScreenController.isSplitScreenVisible()) {
-                    int remainingTaskPosition = mTaskId == mSplitScreenController
-                            .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
-                            ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
-                    ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController
-                            .getTaskInfo(remainingTaskPosition);
+                if (isTaskInSplitScreen(mTaskId)) {
+                    RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
                     mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
                 }
                 decoration.closeMaximizeMenu();
@@ -376,6 +394,7 @@
                     mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
                     decoration.incrementRelayoutBlock();
                     mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct);
+                    closeOtherSplitTask(mTaskId);
                 }
                 decoration.closeHandleMenu();
             } else if (id == R.id.fullscreen_button) {
@@ -720,6 +739,7 @@
                             relevantDecor.mTaskInfo.displayId);
                     if (ev.getY() > statusBarHeight) {
                         if (mMoveToDesktopAnimator == null) {
+                            closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
                             mMoveToDesktopAnimator = new MoveToDesktopAnimator(
                                     mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
                                     relevantDecor.mTaskSurface);
@@ -810,7 +830,8 @@
     private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
         if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) {
             // We can't look at focused task here as only one task will have focus.
-            return getSplitScreenDecor(ev);
+            DesktopModeWindowDecoration splitTaskDecor = getSplitScreenDecor(ev);
+            return splitTaskDecor == null ? getFocusedDecor() : splitTaskDecor;
         } else {
             return getFocusedDecor();
         }
@@ -942,6 +963,24 @@
         }
     }
 
+    private RunningTaskInfo getOtherSplitTask(int taskId) {
+        @SplitPosition int remainingTaskPosition = mSplitScreenController
+                .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
+                ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+        return mSplitScreenController.getTaskInfo(remainingTaskPosition);
+    }
+
+    private void closeOtherSplitTask(int taskId) {
+        if (isTaskInSplitScreen(taskId)) {
+            mTaskOperations.closeTask(getOtherSplitTask(taskId).token);
+        }
+    }
+
+    private boolean isTaskInSplitScreen(int taskId) {
+        return mSplitScreenController != null
+                && mSplitScreenController.isTaskInSplitScreen(taskId);
+    }
+
     private class DragStartListenerImpl
             implements DragPositioningCallbackUtility.DragStartListener {
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index dbff20e..84ec6b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -17,8 +17,10 @@
 package com.android.wm.shell.windowdecor;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
 import android.app.ActivityManager;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -56,6 +58,7 @@
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Supplier;
 
 /**
  * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -109,7 +112,30 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
-        super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig);
+        this (context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+                handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer,
+                SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+                WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
+    }
+
+    DesktopModeWindowDecoration(
+            Context context,
+            DisplayController displayController,
+            ShellTaskOrganizer taskOrganizer,
+            ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl taskSurface,
+            Configuration windowDecorConfig,
+            Handler handler,
+            Choreographer choreographer,
+            SyncTransactionQueue syncQueue,
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+            Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+            Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
+            SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
+        super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+                surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+                windowContainerTransactionSupplier, surfaceControlViewHostFactory);
 
         mHandler = handler;
         mChoreographer = choreographer;
@@ -150,7 +176,7 @@
             return;
         }
 
-        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
         // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
         // synced with the buffer transaction (that draws the View). Both will be shown on screen
         // at the same, whereas applying them independently causes flickering. See b/270202228.
@@ -180,13 +206,23 @@
         mRelayoutParams.reset();
         mRelayoutParams.mRunningTaskInfo = taskInfo;
         mRelayoutParams.mLayoutResId = windowDecorLayoutId;
-        mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+        mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
         mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-
-        mRelayoutParams.mWindowDecorConfig = DesktopTasksController.isDesktopDensityOverrideSet()
-                ? mContext.getResources().getConfiguration() // Use system context
-                : mTaskInfo.configuration; // Use task configuration
+        // The configuration used to lay out the window decoration. The system context's config is
+        // used when the task density has been overridden to a custom density so that the resources
+        // and views of the decoration aren't affected and match the rest of the System UI, if not
+        // then just use the task's configuration. A copy is made instead of using the original
+        // reference so that the configuration isn't mutated on config changes and diff checks can
+        // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+        // See b/301119301.
+        // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+        // instead of using a whole Configuration as a parameter.
+        final Configuration windowDecorConfig = new Configuration();
+        windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+                ? mContext.getResources().getConfiguration() // Use system context.
+                : mTaskInfo.configuration); // Use task configuration.
+        mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
 
         mRelayoutParams.mCornerRadius =
                 (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
@@ -286,7 +322,7 @@
 
         final int displayWidth = displayLayout.width();
         final int displayHeight = displayLayout.height();
-        final int captionHeight = getCaptionHeight();
+        final int captionHeight = getCaptionHeight(mTaskInfo.getWindowingMode());
 
         final ImageButton maximizeWindowButton =
                 mResult.mRootView.findViewById(R.id.maximize_window);
@@ -442,6 +478,7 @@
     @Override
     void releaseViews() {
         closeHandleMenu();
+        closeMaximizeMenu();
         super.releaseViews();
     }
 
@@ -545,7 +582,7 @@
         super.close();
     }
 
-    private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
+    private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FREEFORM
                 ? R.layout.desktop_mode_app_controls_window_decor
                 : R.layout.desktop_mode_focused_window_decor;
@@ -574,7 +611,7 @@
             exclusionRegion = new Region();
         }
         exclusionRegion.union(new Rect(0, 0, mResult.mWidth,
-                getCaptionHeight()));
+                getCaptionHeight(mTaskInfo.getWindowingMode())));
         exclusionRegion.translate(mPositionInParent.x, mPositionInParent.y);
         return exclusionRegion;
     }
@@ -590,12 +627,14 @@
     }
 
     @Override
-    int getCaptionHeightId() {
-        return R.dimen.freeform_decor_caption_height;
+    int getCaptionHeightId(@WindowingMode int windowingMode) {
+        return windowingMode == WINDOWING_MODE_FULLSCREEN
+                ? R.dimen.desktop_mode_fullscreen_decor_caption_height
+                : R.dimen.desktop_mode_freeform_decor_caption_height;
     }
 
-    private int getCaptionHeight() {
-        return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId());
+    private int getCaptionHeight(@WindowingMode int windowingMode) {
+        return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode));
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 1ec8a17..f82b212 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -26,7 +26,10 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.drawable.Drawable;
 import android.view.MotionEvent;
@@ -167,10 +170,9 @@
             desktopBtn.setOnClickListener(mOnClickListener);
             // The button corresponding to the windowing mode that the task is currently in uses a
             // different color than the others.
-            final ColorStateList activeColorStateList = ColorStateList.valueOf(
-                    mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active));
-            final ColorStateList inActiveColorStateList = ColorStateList.valueOf(
-                    mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive));
+            final int[] iconColors = getWindowingIconColor();
+            final ColorStateList inActiveColorStateList = ColorStateList.valueOf(iconColors[0]);
+            final ColorStateList activeColorStateList = ColorStateList.valueOf(iconColors[1]);
             fullscreenBtn.setImageTintList(
                     mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                             ? activeColorStateList : inActiveColorStateList);
@@ -197,6 +199,23 @@
     }
 
     /**
+     * Returns array of windowing icon color based on current UI theme. First element of the
+     * array is for inactive icons and the second is for active icons.
+     */
+    private int[] getWindowingIconColor() {
+        final int mode = mContext.getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_MASK;
+        final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES);
+        final TypedArray typedArray = mContext.obtainStyledAttributes(new int[]{
+                com.android.internal.R.attr.materialColorOnSurface,
+                com.android.internal.R.attr.materialColorPrimary});
+        final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK);
+        final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
+        typedArray.recycle();
+        return new int[] {inActiveColor, activeColor};
+    }
+
+    /**
      * Updates the handle menu pills' position variables to reflect their next positions
      */
     private void updateHandleMenuPillPositions() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 8d76fc6..07fee43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -17,8 +17,10 @@
 package com.android.wm.shell.windowdecor;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -193,12 +195,16 @@
         rootView = null; // Clear it just in case we use it accidentally
 
         final int oldDensityDpi = mWindowDecorConfig.densityDpi;
+        final int oldNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
         mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig
                 : mTaskInfo.getConfiguration();
-        if (oldDensityDpi != mWindowDecorConfig.densityDpi
+        final int newDensityDpi = mWindowDecorConfig.densityDpi;
+        final int newNightMode =  mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        if (oldDensityDpi != newDensityDpi
                 || mDisplay == null
                 || mDisplay.getDisplayId() != mTaskInfo.displayId
-                || oldLayoutResId != mLayoutResId) {
+                || oldLayoutResId != mLayoutResId
+                || oldNightMode != newNightMode) {
             releaseViews();
 
             if (!obtainDisplayOrRegisterListener()) {
@@ -206,6 +212,7 @@
                 return;
             }
             mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig);
+            mDecorWindowContext.setTheme(mContext.getThemeResId());
             if (params.mLayoutResId != 0) {
                 outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
                         .inflate(params.mLayoutResId, null);
@@ -220,6 +227,8 @@
         final Resources resources = mDecorWindowContext.getResources();
         final Configuration taskConfig = mTaskInfo.getConfiguration();
         final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
+        final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode()
+                == WINDOWING_MODE_FULLSCREEN;
         outResult.mWidth = taskBounds.width();
         outResult.mHeight = taskBounds.height();
 
@@ -279,13 +288,24 @@
         mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
         mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
         final Point taskPosition = mTaskInfo.positionInParent;
-        startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
-                .setShadowRadius(mTaskSurface, shadowRadius)
+        if (isFullscreen) {
+            // Setting the task crop to the width/height stops input events from being sent to
+            // some regions of the app window. See b/300324920
+            // TODO(b/296921174): investigate whether crop/position needs to be set by window
+            // decorations at all when transition handlers are already taking ownership of the task
+            // surface placement/crop, especially when in fullscreen where tasks cannot be
+            // drag-resized by the window decoration.
+            startT.setWindowCrop(mTaskSurface, null);
+            finishT.setWindowCrop(mTaskSurface, null);
+        } else {
+            startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+            finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+        }
+        startT.setShadowRadius(mTaskSurface, shadowRadius)
                 .setColor(mTaskSurface, mTmpColor)
                 .show(mTaskSurface);
         finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
-                .setShadowRadius(mTaskSurface, shadowRadius)
-                .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+                .setShadowRadius(mTaskSurface, shadowRadius);
         if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
             finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
@@ -322,7 +342,7 @@
         }
     }
 
-    int getCaptionHeightId() {
+    int getCaptionHeightId(@WindowingMode int windowingMode) {
         return Resources.ID_NULL;
     }
 
@@ -444,7 +464,7 @@
      * Adds caption inset source to a WCT
      */
     public void addCaptionInset(WindowContainerTransaction wct) {
-        final int captionHeightId = getCaptionHeightId();
+        final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
         if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
             return;
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 6fd3354..0058d11 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -119,6 +119,20 @@
     ],
 }
 
+java_library {
+    name: "wm-shell-flicker-platinum-tests",
+    platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
+    srcs: [
+        ":WMShellFlickerServicePlatinumTests-src",
+    ],
+    static_libs: [
+        "wm-shell-flicker-utils",
+    ],
+}
+
 java_defaults {
     name: "WMShellFlickerTestsDefault",
     manifest: "manifests/AndroidManifest.xml",
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index c335d3d..213d596 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -23,6 +23,7 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.common.flicker.subject.region.RegionTraceSubject
 import android.tools.device.helpers.WindowUtils
 import android.tools.device.traces.parsers.toFlickerComponent
 import androidx.test.filters.RequiresDevice
@@ -80,7 +81,9 @@
                 secondAppForSplitScreen.launchViaIntent(wmHelper)
                 pipApp.launchViaIntent(wmHelper)
                 tapl.goHome()
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
+                SplitScreenUtils.enterSplit(
+                    wmHelper, tapl, device, pipApp, secondAppForSplitScreen,
+                    flicker.scenario.startRotation)
                 pipApp.enableAutoEnterForPipActivity()
             }
             teardown {
@@ -126,9 +129,20 @@
         if (tapl.isTablet) {
             flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
         } else {
-            // on phones home does not rotate in landscape, PiP enters back to portrait
-            // orientation so use display bounds from that orientation for assertion
-            flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) }
+            // on phones home screen does not rotate in landscape, PiP enters back to portrait
+            // orientation - if we go from landscape to portrait it should switch between the bounds
+            // otherwise it should be the same as tablet (i.e. portrait to portrait)
+            if (flicker.scenario.isLandscapeOrSeascapeAtStart) {
+                flicker.assertWmVisibleRegion(pipApp) {
+                    // first check against landscape bounds then against portrait bounds
+                    coversAtMost(displayBounds).then().coversAtMost(
+                        portraitDisplayBounds
+                    )
+                }
+            } else {
+                // always check against the display bounds which do not change during transition
+                flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index fb6c093..c9a98c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -33,14 +33,14 @@
     @Postsubmit
     @Test
     override fun pipAppWindowAlwaysVisible() {
-        flicker.assertWm { this.isAppWindowVisible(standardAppHelper) }
+        flicker.assertWm { this.isAppWindowVisible(standardAppHelper.packageNameMatcher) }
     }
 
     /** Checks [standardAppHelper] layer remains visible throughout the animation */
     @Postsubmit
     @Test
     override fun pipAppLayerAlwaysVisible() {
-        flicker.assertLayers { this.isVisible(standardAppHelper) }
+        flicker.assertLayers { this.isVisible(standardAppHelper.packageNameMatcher) }
     }
 
     /** Checks the content overlay appears then disappears during the animation */
@@ -57,7 +57,9 @@
     @Postsubmit
     @Test
     override fun pipWindowRemainInsideVisibleBounds() {
-        flicker.assertWmVisibleRegion(standardAppHelper) { coversAtMost(displayBounds) }
+        flicker.assertWmVisibleRegion(standardAppHelper.packageNameMatcher) {
+            coversAtMost(displayBounds)
+        }
     }
 
     /**
@@ -68,7 +70,7 @@
     @Test
     override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
         flicker.assertLayersVisibleRegion(
-            standardAppHelper.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+            standardAppHelper.packageNameMatcher.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
         ) {
             coversAtMost(displayBounds)
         }
@@ -93,9 +95,9 @@
     @Test
     override fun pipWindowBecomesPinned() {
         flicker.assertWm {
-            invoke("pipWindowIsNotPinned") { it.isNotPinned(standardAppHelper) }
+            invoke("pipWindowIsNotPinned") { it.isNotPinned(standardAppHelper.packageNameMatcher) }
                 .then()
-                .invoke("pipWindowIsPinned") { it.isPinned(standardAppHelper) }
+                .invoke("pipWindowIsPinned") { it.isPinned(standardAppHelper.packageNameMatcher) }
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index 54b3e2a8..d7ba3d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -16,19 +16,22 @@
 
 package com.android.wm.shell.flicker.pip.apps
 
-import android.os.Handler
-import android.os.Looper
-import android.os.SystemClock
 import android.content.Context
 import android.location.Criteria
 import android.location.Location
 import android.location.LocationManager
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.platform.test.annotations.Postsubmit
 import android.tools.device.apphelpers.MapsAppHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import androidx.test.filters.RequiresDevice
+import org.junit.Assume
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -129,4 +132,12 @@
     override val thisTransition: FlickerBuilder.() -> Unit = {
         transitions { tapl.goHome() }
     }
+
+    @Postsubmit
+    @Test
+    override fun focusChanges() {
+        // in gestural nav the focus goes to different activity on swipe up with auto enter PiP
+        Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+        super.focusChanges()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
new file mode 100644
index 0000000..c370d91
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.flicker.pip.apps
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.YouTubeAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.RequiresDevice
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from YouTube app by interacting with the app UI
+ *
+ * To run this test: `atest WMShellFlickerTests:YouTubeEnterPipTest`
+ *
+ * Actions:
+ * ```
+ *     Launch YouTube and start playing a video
+ *     Go home to enter PiP
+ * ```
+ *
+ * Notes:
+ * ```
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited from [PipTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
+    override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
+
+    override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+        setup {
+            standardAppHelper.launchViaIntent(
+                wmHelper,
+                YouTubeAppHelper.getYoutubeVideoIntent("HPcEAtoXXLA"),
+                ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "")
+            )
+            standardAppHelper.waitForVideoPlaying()
+        }
+    }
+
+    override val defaultTeardown: FlickerBuilder.() -> Unit = {
+        teardown {
+            standardAppHelper.exit(wmHelper)
+        }
+    }
+
+    override val thisTransition: FlickerBuilder.() -> Unit = {
+        transitions { tapl.goHome() }
+    }
+
+    @Postsubmit
+    @Test
+    override fun pipOverlayLayerAppearThenDisappear() {
+        // YouTube uses source rect hint, so PiP overlay is never present
+    }
+
+    @Postsubmit
+    @Test
+    override fun focusChanges() {
+        // in gestural nav the focus goes to different activity on swipe up with auto enter PiP
+        Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+        super.focusChanges()
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
index fa1be63..2539fd5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -31,7 +31,8 @@
 class DismissSplitScreenByDividerGesturalNavPortrait :
     DismissSplitScreenByDivider(Rotation.ROTATION_0) {
 
-    @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+    // TODO(b/300260196): Not detecting this scenario right now
+    @ExpectedScenarios(["ENTIRE_TRACE"])
     @Test
     override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index aa35237..a8f4d0a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -31,7 +31,7 @@
 class DismissSplitScreenByGoHomeGesturalNavLandscape :
     DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
 
-    @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
     @Test
     override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index e195360..cee9bbf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
 class DismissSplitScreenByGoHomeGesturalNavPortrait :
     DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
 
-    @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
     @Test
     override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 5f771c7..169b5cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -31,7 +31,7 @@
 class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
     EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
 
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
     @Test
     override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 729a401..412c011 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -31,7 +31,7 @@
 class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
     EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
 
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
     @Test
     override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index e37d806..b444bad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -19,10 +19,15 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
 import org.junit.Test
 
 open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 2a50912..e2ab989 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -19,10 +19,15 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
 import org.junit.Test
 
 open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index d5da1a8..22b8102 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
 import org.junit.Test
 
 open class DismissSplitScreenByDividerGesturalNavLandscape :
     DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 7fdcb9b..3fb014f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
 import org.junit.Test
 
 open class DismissSplitScreenByDividerGesturalNavPortrait :
     DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 308e954..ea1f942 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
 import org.junit.Test
 
 open class DismissSplitScreenByGoHomeGesturalNavLandscape :
     DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 39e75bd..8f23a79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
 import org.junit.Test
 
 open class DismissSplitScreenByGoHomeGesturalNavPortrait :
     DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index e18da17..b0f39e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -19,10 +19,15 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
 import org.junit.Test
 
 open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 00d60e7..6ce8746 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -19,10 +19,15 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
 import org.junit.Test
 
 open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index d7efbc8..9f74edf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
     EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 4eece3f..1e4055e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
     EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index d96b056..c3b8132 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
     EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 809b690..7756d04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
     EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index bbdf2d7..c72aa5a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
     EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 5c29fd8..cc88f27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
     EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index a7398eb..87b38b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
     EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index eae88ad..ca347ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
     EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 7e8ee04..6597819 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenFromOverviewGesturalNavLandscape :
     EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 9295c33..6df31fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
 import org.junit.Test
 
 open class EnterSplitScreenFromOverviewGesturalNavPortrait :
     EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 4b59e9f..02596c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
     SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 5ff36d4..9d579f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
     SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index c0cb721..da85342 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
     SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index 8c14088..1ae2c9e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
     SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 7b6614b..b1b56257 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBackToSplitFromHomeGesturalNavLandscape :
     SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 5df5be9..08c437e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBackToSplitFromHomeGesturalNavPortrait :
     SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 9d63003..efbf86d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBackToSplitFromRecentGesturalNavLandscape :
     SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 9fa04b2..f7072fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBackToSplitFromRecentGesturalNavPortrait :
     SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index 9386aa2..d80d112 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBetweenSplitPairsGesturalNavLandscape :
     SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index 5ef2167..30ec37a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -19,11 +19,16 @@
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
 import org.junit.Test
 
 open class SwitchBetweenSplitPairsGesturalNavPortrait :
     SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 9caab9b..1e086d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -18,13 +18,18 @@
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.BlockJUnit4ClassRunner
 
 @RunWith(BlockJUnit4ClassRunner::class)
 open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index bf484e5..932f892 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -18,13 +18,18 @@
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.rules.FlickerServiceRule
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.BlockJUnit4ClassRunner
 
 @RunWith(BlockJUnit4ClassRunner::class)
 open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+    @get:Rule
+    val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
index 245184c..80ab24d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -51,7 +51,7 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
index 1f2f1ec..4c39104 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -49,7 +49,7 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
index ebbf7c5..f6d1afc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -49,7 +49,7 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
index 71e701c..db5a32a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -49,7 +49,7 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
index c433b21..d7b306c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -49,11 +50,13 @@
     fun setup() {
         Assume.assumeTrue(tapl.isTablet)
 
+        tapl.goHome()
+
+        primaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
-
-        tapl.goHome()
-        primaryApp.launchViaIntent(wmHelper)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index 3f087a5..cc982d1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -50,14 +51,16 @@
     fun setup() {
         Assume.assumeTrue(tapl.isTablet)
 
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
         // Send a notification
         sendNotificationApp.launchViaIntent(wmHelper)
         sendNotificationApp.postNotification(wmHelper)
         tapl.goHome()
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
         primaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
index 767e7b5..fa12bb8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
@@ -49,12 +50,13 @@
     fun setup() {
         Assume.assumeTrue(tapl.isTablet)
 
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
         tapl.goHome()
         SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
         primaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
index f2cbf24..983653b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -46,9 +46,6 @@
 
     @Before
     fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
         primaryApp.launchViaIntent(wmHelper)
         secondaryApp.launchViaIntent(wmHelper)
         tapl.goHome()
@@ -57,11 +54,14 @@
             .withAppTransitionIdle()
             .withHomeActivityVisible()
             .waitForAndVerify()
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
     }
 
     @Test
     open fun enterSplitScreenFromOverview() {
-        SplitScreenUtils.splitFromOverview(tapl, device)
+        SplitScreenUtils.splitFromOverview(tapl, device, rotation)
         SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 538ed96..068171d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -48,11 +48,12 @@
 
     @Before
     fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
         tapl.workspace.switchToOverview().dismissAllTasks()
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 0dab5ad..64b75c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -50,7 +50,7 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
 
         thirdApp.launchViaIntent(wmHelper)
         wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
index ad3a2d4..1795010 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -49,7 +49,7 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
 
         tapl.goHome()
         wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index b780a16..7065846 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -46,11 +46,12 @@
 
     @Before
     fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
         tapl.workspace.switchToOverview().dismissAllTasks()
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
 
         tapl.goHome()
         wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
index 329d61d..251cb50 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -51,8 +51,8 @@
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation)
         SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
index e59ed64..1387536 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -62,8 +62,10 @@
         get() = {
             setup {
                 tapl.goHome()
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                    secondaryApp, flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp,
+                    flicker.scenario.startRotation)
                 pipApp.enableAutoEnterForPipActivity()
                 SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp)
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index 4d90070..3b9e53f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -39,7 +39,8 @@
     protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
+            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                textEditApp, flicker.scenario.startRotation) }
             transitions {
                 SplitScreenUtils.copyContentInSplit(
                     instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 8360e94..5fdde3a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -35,7 +35,8 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                secondaryApp, flicker.scenario.startRotation) }
             transitions {
                 if (tapl.isTablet) {
                     SplitScreenUtils.dragDividerToDismissSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index e745878..b7f6bfe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -35,7 +35,10 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+            setup {
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp,
+                    flicker.scenario.startRotation)
+            }
             transitions {
                 tapl.goHome()
                 wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index c3beb36..bb2a7aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -37,7 +37,8 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                secondaryApp, flicker.scenario.startRotation) }
             transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index be507d8..5e5e7d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -46,7 +46,7 @@
                     .waitForAndVerify()
             }
             transitions {
-                SplitScreenUtils.splitFromOverview(tapl, device)
+                SplitScreenUtils.splitFromOverview(tapl, device, flicker.scenario.startRotation)
                 SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
             }
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index ed0debd..46b0bd2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -39,7 +39,8 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                secondaryApp, flicker.scenario.startRotation) }
             transitions {
                 SplitScreenUtils.doubleTapDividerToSwitch(device)
                 wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 9b7939a..baf7693 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -39,7 +39,8 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                    secondaryApp, flicker.scenario.startRotation)
 
                 thirdApp.launchViaIntent(wmHelper)
                 wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index 9326ef3..33b55f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -37,7 +37,8 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                    secondaryApp, flicker.scenario.startRotation)
 
                 tapl.goHome()
                 wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index b928e40..b79dfb5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -37,7 +37,8 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                    secondaryApp, flicker.scenario.startRotation)
 
                 tapl.goHome()
                 wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index f314995..0204d75 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -39,8 +39,10 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+                    secondaryApp, flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp,
+                    flicker.scenario.startRotation)
                 SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
             }
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 3f8a1ae..6b3cfaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.graphics.Point
 import android.os.SystemClock
+import android.tools.common.Rotation
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.common.traces.component.IComponentMatcher
 import android.tools.common.traces.component.IComponentNameMatcher
@@ -41,6 +42,7 @@
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
 import org.junit.Assert.assertNotNull
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 
 object SplitScreenUtils {
     private const val TIMEOUT_MS = 3_000L
@@ -101,13 +103,15 @@
         tapl: LauncherInstrumentation,
         device: UiDevice,
         primaryApp: StandardAppHelper,
-        secondaryApp: StandardAppHelper
+        secondaryApp: StandardAppHelper,
+        rotation: Rotation
     ) {
         primaryApp.launchViaIntent(wmHelper)
         secondaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
         tapl.goHome()
         wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
-        splitFromOverview(tapl, device)
+        splitFromOverview(tapl, device, rotation)
         waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
     }
 
@@ -121,7 +125,7 @@
         waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
     }
 
-    fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
+    fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice, rotation: Rotation) {
         // Note: The initial split position in landscape is different between tablet and phone.
         // In landscape, tablet will let the first app split to right side, and phone will
         // split to left side.
@@ -129,7 +133,9 @@
             // TAPL's currentTask on tablet is sometimes not what we expected if the overview
             // contains more than 3 task views. We need to use uiautomator directly to find the
             // second task to split.
-            tapl.workspace.switchToOverview().overviewActions.clickSplit()
+            val home = tapl.workspace.switchToOverview()
+            ChangeDisplayOrientationRule.setRotation(rotation)
+            home.overviewActions.clickSplit()
             val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
             if (snapshots == null || snapshots.size < 1) {
                 error("Fail to find a overview snapshot to split.")
@@ -145,9 +151,13 @@
             }
             snapshots[0].click()
         } else {
-            tapl.workspace
+            val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled()
+            tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview
+            val home = tapl.workspace
                 .switchToOverview()
-                .currentTask
+            tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks
+            ChangeDisplayOrientationRule.setRotation(rotation)
+            home.currentTask
                 .tapMenu()
                 .tapSplitMenuItem()
                 .currentTask
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 17ed396..e727491 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -424,6 +424,7 @@
                         eq(-5f), anyFloat(), eq(true))
     }
 
+    @Ignore("Started flaking despite no changes, tracking in b/299636216")
     @Test
     fun testIsPropertyAnimating() {
         PhysicsAnimatorTestUtils.setAllAnimationsBlock(false)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 9f0d89b..5237585 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -27,15 +27,13 @@
 import junit.framework.Assert.assertEquals
 import org.junit.Before
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -66,7 +64,7 @@
 
     @Before
     fun setup() {
-        launcherApps = mock(LauncherApps::class.java)
+        launcherApps = mock<LauncherApps>()
         repository = BubbleVolatileRepository(launcherApps)
     }
 
@@ -98,7 +96,7 @@
         repository.addBubbles(user11.identifier, listOf(bubble12))
         assertEquals(listOf(bubble11, bubble12), repository.getEntities(user11.identifier))
 
-        Mockito.verifyNoMoreInteractions(launcherApps)
+        verifyNoMoreInteractions(launcherApps)
     }
 
     @Test
@@ -167,9 +165,8 @@
         assertThat(ret).isTrue() // bubbles were removed
 
         assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
-        verify(launcherApps, never()).uncacheShortcuts(anyString(),
-                any(),
-                any(UserHandle::class.java), anyInt())
+        verify(launcherApps, never())
+                .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
     }
 
     @Test
@@ -184,9 +181,8 @@
 
         assertThat(repository.getEntities(user0.identifier).toList())
                 .isEqualTo(listOf(bubble1, bubble3))
-        verify(launcherApps, never()).uncacheShortcuts(anyString(),
-                any(),
-                any(UserHandle::class.java), anyInt())
+        verify(launcherApps, never())
+                .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
     }
 
     @Test
@@ -200,9 +196,8 @@
 
         assertThat(repository.getEntities(user0.identifier).toList())
                 .isEqualTo(listOf(bubble1, bubble2, bubble3))
-        verify(launcherApps, never()).uncacheShortcuts(anyString(),
-                any(),
-                any(UserHandle::class.java), anyInt())
+        verify(launcherApps, never())
+                .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
     }
 
     @Test
@@ -219,9 +214,8 @@
                 user11.identifier))
         assertThat(ret).isFalse() // bubbles were NOT removed
 
-        verify(launcherApps, never()).uncacheShortcuts(anyString(),
-                any(),
-                any(UserHandle::class.java), anyInt())
+        verify(launcherApps, never())
+                .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
     }
 
     @Test
@@ -237,9 +231,8 @@
         assertThat(ret).isTrue() // bubbles were removed
 
         assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
-        verify(launcherApps, never()).uncacheShortcuts(anyString(),
-                any(),
-                any(UserHandle::class.java), anyInt())
+        verify(launcherApps, never())
+                .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
 
         // User 11 bubbles should still be here
         assertThat(repository.getEntities(user11.identifier).toList())
@@ -261,9 +254,8 @@
         // bubble2 is the work profile bubble and should be removed
         assertThat(repository.getEntities(user0.identifier).toList())
                 .isEqualTo(listOf(bubble1, bubble3))
-        verify(launcherApps, never()).uncacheShortcuts(anyString(),
-                any(),
-                any(UserHandle::class.java), anyInt())
+        verify(launcherApps, never())
+                .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
 
         // User 11 bubbles should still be here
         assertThat(repository.getEntities(user11.identifier).toList())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index ad84c7f..56d0f8e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -18,9 +18,13 @@
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -130,7 +134,7 @@
     @Test
     public void testSetDivideRatio() {
         mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
-        mSplitLayout.setDivideRatio(0.5f);
+        mSplitLayout.setDivideRatio(SNAP_TO_50_50);
         assertThat(mSplitLayout.getDividePosition()).isEqualTo(
                 mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index be4a287..c6cccc0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -52,6 +52,8 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
@@ -94,6 +96,7 @@
             ToggleResizeDesktopTaskTransitionHandler
     @Mock lateinit var launchAdjacentController: LaunchAdjacentController
     @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
+    @Mock lateinit var splitScreenController: SplitScreenController
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var controller: DesktopTasksController
@@ -116,6 +119,7 @@
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
 
         controller = createController()
+        controller.splitScreenController = splitScreenController
 
         shellInit.init()
     }
@@ -341,6 +345,30 @@
     }
 
     @Test
+    fun moveToDesktop_splitTaskExitsSplit() {
+        var task = setUpSplitScreenTask()
+        controller.moveToDesktop(desktopModeWindowDecoration, task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
+            eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+        )
+    }
+
+    @Test
+    fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
+        var task = setUpFullscreenTask()
+        controller.moveToDesktop(desktopModeWindowDecoration, task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
+            eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+        )
+    }
+
+    @Test
     fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
@@ -695,6 +723,13 @@
         return task
     }
 
+    private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+        val task = createSplitScreenTask(displayId)
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        runningTasks.add(task)
+        return task
+    }
+
     private fun markTaskVisible(task: RunningTaskInfo) {
         desktopModeTaskRepository.updateVisibleFreeformTasks(
             task.displayId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 29a757c..2f6f320 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -21,6 +21,7 @@
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.view.Display.DEFAULT_DISPLAY
 import com.android.wm.shell.MockToken
 import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -45,12 +46,25 @@
         @JvmOverloads
         fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
             return TestRunningTaskInfoBuilder()
-                    .setDisplayId(displayId)
-                    .setToken(MockToken().token())
-                    .setActivityType(ACTIVITY_TYPE_STANDARD)
-                    .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                    .setLastActiveTime(100)
-                    .build()
+                .setDisplayId(displayId)
+                .setToken(MockToken().token())
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .setLastActiveTime(100)
+                .build()
+        }
+
+        /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */
+        @JvmStatic
+        @JvmOverloads
+        fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+            return TestRunningTaskInfoBuilder()
+                .setDisplayId(displayId)
+                .setToken(MockToken().token())
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+                .setLastActiveTime(100)
+                .build()
         }
 
         /** Create a new home task */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
index baa06f2..bbd65be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -24,6 +24,7 @@
 import android.window.WindowContainerToken
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
 import com.android.wm.shell.util.GroupedRecentTaskInfo
 import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
 import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
@@ -123,6 +124,7 @@
         assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
         assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
         assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
+        assertThat(recentTaskInfoParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_50_50)
     }
 
     @Test
@@ -156,7 +158,7 @@
     private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
         val task1 = createTaskInfo(id = 1)
         val task2 = createTaskInfo(id = 2)
-        val splitBounds = SplitBounds(Rect(), Rect(), 1, 2)
+        val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_50_50)
         return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 40ce785..10e9e11 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -173,10 +174,10 @@
 
         // Verify only one update if the split info is the same
         SplitBounds bounds1 = new SplitBounds(new Rect(0, 0, 50, 50),
-                new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+                new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50);
         mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
         SplitBounds bounds2 = new SplitBounds(new Rect(0, 0, 50, 50),
-                new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+                new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50);
         mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
         verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
     }
@@ -207,8 +208,10 @@
         setRawList(t1, t2, t3, t4, t5, t6);
 
         // Mark a couple pairs [t2, t4], [t3, t5]
-        SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
-        SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+        SplitBounds pair1Bounds =
+                new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50);
+        SplitBounds pair2Bounds =
+                new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50);
 
         mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
         mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -236,8 +239,10 @@
         setRawList(t1, t2, t3, t4, t5, t6);
 
         // Mark a couple pairs [t2, t4], [t3, t5]
-        SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
-        SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+        SplitBounds pair1Bounds =
+                new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50);
+        SplitBounds pair2Bounds =
+                new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50);
 
         mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
         mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -334,7 +339,8 @@
         setRawList(t1, t2, t3);
 
         // Add a pair
-        SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 3);
+        SplitBounds pair1Bounds =
+                new SplitBounds(new Rect(), new Rect(), 2, 3, SNAP_TO_50_50);
         mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
         reset(mRecentTasksController);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
index 50d02ae..b790aee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
@@ -1,5 +1,7 @@
 package com.android.wm.shell.recents;
 
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -44,21 +46,21 @@
     @Test
     public void testVerticalStacked() {
         SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
-                TASK_ID_1, TASK_ID_2);
+                TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
         assertTrue(ssb.appsStackedVertically);
     }
 
     @Test
     public void testHorizontalStacked() {
         SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
-                TASK_ID_1, TASK_ID_2);
+                TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
         assertFalse(ssb.appsStackedVertically);
     }
 
     @Test
     public void testHorizontalDividerBounds() {
         SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
-                TASK_ID_1, TASK_ID_2);
+                TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
         Rect dividerBounds = ssb.visualDividerBounds;
         assertEquals(0, dividerBounds.left);
         assertEquals(DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2, dividerBounds.top);
@@ -69,7 +71,7 @@
     @Test
     public void testVerticalDividerBounds() {
         SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
-                TASK_ID_1, TASK_ID_2);
+                TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
         Rect dividerBounds = ssb.visualDividerBounds;
         assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left);
         assertEquals(0, dividerBounds.top);
@@ -80,7 +82,7 @@
     @Test
     public void testEqualVerticalTaskPercent() {
         SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
-                TASK_ID_1, TASK_ID_2);
+                TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
         float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH;
         assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01);
     }
@@ -88,7 +90,7 @@
     @Test
     public void testEqualHorizontalTaskPercent() {
         SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
-                TASK_ID_1, TASK_ID_2);
+                TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
         float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
         assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index d098d33..0088051 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -221,6 +221,20 @@
     }
 
     @Test
+    public void testSurfaceDestroyed_withTask_shouldNotHideTask_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
+        mTaskViewTaskController.setHideTaskWithSurface(false);
+
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(sh);
+        reset(mViewListener);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
     public void testSurfaceDestroyed_withTask_legacyTransitions() {
         assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         SurfaceHolder sh = mock(SurfaceHolder.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
deleted file mode 100644
index d8afe68..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2022 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.windowdecor;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.InputMonitor;
-import android.view.SurfaceControl;
-import android.view.SurfaceView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopTasksController;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/** Tests of {@link DesktopModeWindowDecorViewModel} */
-@SmallTest
-public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
-
-    private static final String TAG = "DesktopModeWindowDecorViewModelTests";
-    private static  final Rect STABLE_INSETS = new Rect(0, 100, 0, 0);
-
-    @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
-    @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
-
-    @Mock private Handler mMainHandler;
-    @Mock private Choreographer mMainChoreographer;
-    @Mock private ShellTaskOrganizer mTaskOrganizer;
-    @Mock private DisplayController mDisplayController;
-    @Mock private DisplayLayout mDisplayLayout;
-    @Mock private SyncTransactionQueue mSyncQueue;
-    @Mock private DesktopTasksController mDesktopTasksController;
-    @Mock private InputMonitor mInputMonitor;
-    @Mock private InputManager mInputManager;
-    @Mock private Transitions mTransitions;
-    @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
-    @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
-    @Mock private SurfaceControl.Transaction mTransaction;
-    @Mock private Display mDisplay;
-    @Mock private ShellController mShellController;
-    @Mock private ShellInit mShellInit;
-    @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
-            mDesktopModeKeyguardChangeListener;
-    @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
-    private final List<InputManager> mMockInputManagers = new ArrayList<>();
-
-    private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
-
-    @Before
-    public void setUp() {
-        mMockInputManagers.add(mInputManager);
-
-        mDesktopModeWindowDecorViewModel =
-                new DesktopModeWindowDecorViewModel(
-                        mContext,
-                        mMainHandler,
-                        mMainChoreographer,
-                        mShellInit,
-                        mTaskOrganizer,
-                        mDisplayController,
-                        mShellController,
-                        mSyncQueue,
-                        mTransitions,
-                        Optional.of(mDesktopTasksController),
-                        mDesktopModeWindowDecorFactory,
-                        mMockInputMonitorFactory,
-                        mTransactionFactory,
-                        mDesktopModeKeyguardChangeListener,
-                        mRootTaskDisplayAreaOrganizer
-                );
-
-        doReturn(mDesktopModeWindowDecoration)
-                .when(mDesktopModeWindowDecorFactory)
-                .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
-        doReturn(mTransaction).when(mTransactionFactory).get();
-        doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
-        doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
-        doNothing().when(mShellController).addKeyguardChangeListener(any());
-
-        when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
-        // InputChannel cannot be mocked because it passes to InputEventReceiver.
-        final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
-        inputChannels[0].dispose();
-        when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
-
-        mDesktopModeWindowDecoration.mDisplay = mDisplay;
-        doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId();
-    }
-
-    @Test
-    public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
-        final int taskId = 1;
-        final ActivityManager.RunningTaskInfo taskInfo =
-                createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
-        SurfaceControl surfaceControl = mock(SurfaceControl.class);
-        runOnMainThread(() -> {
-            final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
-            final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
-            mDesktopModeWindowDecorViewModel.onTaskOpening(
-                    taskInfo, surfaceControl, startT, finishT);
-
-            taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-            mDesktopModeWindowDecorViewModel.onTaskChanging(
-                    taskInfo, surfaceControl, startT, finishT);
-        });
-        verify(mDesktopModeWindowDecorFactory)
-                .create(
-                        mContext,
-                        mDisplayController,
-                        mTaskOrganizer,
-                        taskInfo,
-                        surfaceControl,
-                        mMainHandler,
-                        mMainChoreographer,
-                        mSyncQueue,
-                        mRootTaskDisplayAreaOrganizer);
-        verify(mDesktopModeWindowDecoration).close();
-    }
-
-    @Test
-    public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
-        final int taskId = 1;
-        final ActivityManager.RunningTaskInfo taskInfo =
-                createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_UNDEFINED);
-        SurfaceControl surfaceControl = mock(SurfaceControl.class);
-        runOnMainThread(() -> {
-            final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
-            final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-            taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-
-            mDesktopModeWindowDecorViewModel.onTaskChanging(
-                    taskInfo, surfaceControl, startT, finishT);
-
-            taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-            taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
-
-            mDesktopModeWindowDecorViewModel.onTaskChanging(
-                    taskInfo, surfaceControl, startT, finishT);
-        });
-        verify(mDesktopModeWindowDecorFactory, times(1))
-                .create(
-                        mContext,
-                        mDisplayController,
-                        mTaskOrganizer,
-                        taskInfo,
-                        surfaceControl,
-                        mMainHandler,
-                        mMainChoreographer,
-                        mSyncQueue,
-                        mRootTaskDisplayAreaOrganizer);
-    }
-
-    @Test
-    public void testCreateAndDisposeEventReceiver() throws Exception {
-        final int taskId = 1;
-        final ActivityManager.RunningTaskInfo taskInfo =
-                createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
-        taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
-        runOnMainThread(() -> {
-            SurfaceControl surfaceControl = mock(SurfaceControl.class);
-            final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
-            final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
-            mDesktopModeWindowDecorViewModel.onTaskOpening(
-                    taskInfo, surfaceControl, startT, finishT);
-
-            mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
-        });
-        verify(mMockInputMonitorFactory).create(any(), any());
-        verify(mInputMonitor).dispose();
-    }
-
-    @Test
-    public void testEventReceiversOnMultipleDisplays() throws Exception {
-        runOnMainThread(() -> {
-            SurfaceView surfaceView = new SurfaceView(mContext);
-            final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
-            final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay(
-                    "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400,
-                    /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
-                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
-            try {
-                int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId();
-
-                final int taskId = 1;
-                final ActivityManager.RunningTaskInfo taskInfo =
-                        createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
-                final ActivityManager.RunningTaskInfo secondTaskInfo =
-                        createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
-                final ActivityManager.RunningTaskInfo thirdTaskInfo =
-                        createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
-
-                SurfaceControl surfaceControl = mock(SurfaceControl.class);
-                final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
-                final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
-                mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT,
-                        finishT);
-                mDesktopModeWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl,
-                        startT, finishT);
-                mDesktopModeWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl,
-                        startT, finishT);
-                mDesktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo);
-                mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
-            } finally {
-                secondaryDisplay.release();
-            }
-        });
-        verify(mMockInputMonitorFactory, times(2)).create(any(), any());
-        verify(mInputMonitor, times(1)).dispose();
-    }
-
-    @Test
-    public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception {
-        doReturn(true).when(
-                mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded();
-
-        final int taskId = 1;
-        final ActivityManager.RunningTaskInfo taskInfo =
-                createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN);
-        taskInfo.isFocused = true;
-        SurfaceControl surfaceControl = mock(SurfaceControl.class);
-        runOnMainThread(() -> {
-            final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
-            final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
-            mDesktopModeWindowDecorViewModel.onTaskOpening(
-                    taskInfo, surfaceControl, startT, finishT);
-
-            taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-            mDesktopModeWindowDecorViewModel.onTaskChanging(
-                    taskInfo, surfaceControl, startT, finishT);
-        });
-        verify(mDesktopModeWindowDecorFactory, never())
-                .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
-    }
-
-    private void runOnMainThread(Runnable r) throws Exception {
-        final Handler mainHandler = new Handler(Looper.getMainLooper());
-        final CountDownLatch latch = new CountDownLatch(1);
-        mainHandler.post(() -> {
-            r.run();
-            latch.countDown();
-        });
-        latch.await(1, TimeUnit.SECONDS);
-    }
-
-    private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId,
-            int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
-        ActivityManager.RunningTaskInfo taskInfo =
-                new TestRunningTaskInfoBuilder()
-                        .setDisplayId(displayId)
-                        .setVisible(true)
-                        .build();
-        taskInfo.taskId = taskId;
-        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
-        return taskInfo;
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
new file mode 100644
index 0000000..00d70a7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 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.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.os.Handler
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.InputChannel
+import android.view.InputMonitor
+import android.view.SurfaceControl
+import android.view.SurfaceView
+import androidx.core.content.getSystemService
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.sysui.KeyguardChangeListener
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import java.util.Optional
+import java.util.function.Supplier
+
+/** Tests of [DesktopModeWindowDecorViewModel]  */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
+    @Mock private lateinit var mockDesktopModeWindowDecorFactory:
+            DesktopModeWindowDecoration.Factory
+    @Mock private lateinit var mockMainHandler: Handler
+    @Mock private lateinit var mockMainChoreographer: Choreographer
+    @Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer
+    @Mock private lateinit var mockDisplayController: DisplayController
+    @Mock private lateinit var mockDisplayLayout: DisplayLayout
+    @Mock private lateinit var mockSyncQueue: SyncTransactionQueue
+    @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+    @Mock private lateinit var mockInputMonitor: InputMonitor
+    @Mock private lateinit var mockTransitions: Transitions
+    @Mock private lateinit var mockInputMonitorFactory:
+            DesktopModeWindowDecorViewModel.InputMonitorFactory
+    @Mock private lateinit var mockShellController: ShellController
+    @Mock private lateinit var mockShellExecutor: ShellExecutor
+    @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler
+
+    private val transactionFactory = Supplier<SurfaceControl.Transaction> {
+        SurfaceControl.Transaction()
+    }
+
+    private lateinit var shellInit: ShellInit
+    private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
+
+    @Before
+    fun setUp() {
+        shellInit = ShellInit(mockShellExecutor)
+        desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
+                mContext,
+                mockMainHandler,
+                mockMainChoreographer,
+                shellInit,
+                mockTaskOrganizer,
+                mockDisplayController,
+                mockShellController,
+                mockSyncQueue,
+                mockTransitions,
+                Optional.of(mockDesktopTasksController),
+                mockRecentsTransitionHandler,
+                mockDesktopModeWindowDecorFactory,
+                mockInputMonitorFactory,
+                transactionFactory,
+                mockRootTaskDisplayAreaOrganizer
+        )
+
+        whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
+        whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
+        whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
+
+        // InputChannel cannot be mocked because it passes to InputEventReceiver.
+        val inputChannels = InputChannel.openInputChannelPair(TAG)
+        inputChannels.first().dispose()
+        whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1])
+
+        shellInit.init()
+    }
+
+    @Test
+    fun testDeleteCaptionOnChangeTransitionWhenNecessary() {
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+        val taskSurface = SurfaceControl()
+        val decoration = setUpMockDecorationForTask(task)
+
+        onTaskOpening(task, taskSurface)
+
+        task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+        task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
+        onTaskChanging(task, taskSurface)
+
+        verify(mockDesktopModeWindowDecorFactory).create(
+                mContext,
+                mockDisplayController,
+                mockTaskOrganizer,
+                task,
+                taskSurface,
+                mockMainHandler,
+                mockMainChoreographer,
+                mockSyncQueue,
+                mockRootTaskDisplayAreaOrganizer
+        )
+        verify(decoration).close()
+    }
+
+    @Test
+    fun testCreateCaptionOnChangeTransitionWhenNecessary() {
+        val task = createTask(
+                windowingMode = WINDOWING_MODE_UNDEFINED,
+                activityType = ACTIVITY_TYPE_UNDEFINED
+        )
+        val taskSurface = SurfaceControl()
+        setUpMockDecorationForTask(task)
+
+        onTaskChanging(task, taskSurface)
+        verify(mockDesktopModeWindowDecorFactory, never()).create(
+                mContext,
+                mockDisplayController,
+                mockTaskOrganizer,
+                task,
+                taskSurface,
+                mockMainHandler,
+                mockMainChoreographer,
+                mockSyncQueue,
+                mockRootTaskDisplayAreaOrganizer
+        )
+
+        task.setWindowingMode(WINDOWING_MODE_FREEFORM)
+        task.setActivityType(ACTIVITY_TYPE_STANDARD)
+        onTaskChanging(task, taskSurface)
+        verify(mockDesktopModeWindowDecorFactory, times(1)).create(
+                mContext,
+                mockDisplayController,
+                mockTaskOrganizer,
+                task,
+                taskSurface,
+                mockMainHandler,
+                mockMainChoreographer,
+                mockSyncQueue,
+                mockRootTaskDisplayAreaOrganizer
+        )
+    }
+
+    @Test
+    fun testCreateAndDisposeEventReceiver() {
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+        setUpMockDecorationForTask(task)
+
+        onTaskOpening(task)
+        desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+
+        verify(mockInputMonitorFactory).create(any(), any())
+        verify(mockInputMonitor).dispose()
+    }
+
+    @Test
+    fun testEventReceiversOnMultipleDisplays() {
+        val secondaryDisplay = createVirtualDisplay() ?: return
+        val secondaryDisplayId = secondaryDisplay.display.displayId
+        val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM)
+        val secondTask = createTask(
+                displayId = secondaryDisplayId,
+                windowingMode = WINDOWING_MODE_FREEFORM
+        )
+        val thirdTask = createTask(
+                displayId = secondaryDisplayId,
+                windowingMode = WINDOWING_MODE_FREEFORM
+        )
+        setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+        onTaskOpening(task)
+        onTaskOpening(secondTask)
+        onTaskOpening(thirdTask)
+        desktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTask)
+        desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+        secondaryDisplay.release()
+
+        verify(mockInputMonitorFactory, times(2)).create(any(), any())
+        verify(mockInputMonitor, times(1)).dispose()
+    }
+
+    @Test
+    fun testCaptionIsNotCreatedWhenKeyguardIsVisible() {
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>()
+        verify(mockShellController).addKeyguardChangeListener(keyguardListenerCaptor.capture())
+
+        keyguardListenerCaptor.firstValue.onKeyguardVisibilityChanged(
+                true /* visible */,
+                true /* occluded */,
+                false /* animatingDismiss */
+        )
+        onTaskOpening(task)
+
+        task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+        task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED)
+        onTaskChanging(task)
+
+        verify(mockDesktopModeWindowDecorFactory, never())
+                .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    fun testRelayoutBlockedDuringRecentsTransition() {
+        val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>()
+        verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture())
+
+        val transition = mock(IBinder::class.java)
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+        val decoration = setUpMockDecorationForTask(task)
+
+        // Make sure a window decorations exists first by launching a freeform task.
+        onTaskOpening(task)
+        // Now call back when a Recents transition starts.
+        recentsCaptor.firstValue.onTransitionStarted(transition)
+
+        verify(decoration).incrementRelayoutBlock()
+        verify(decoration).addTransitionPausingRelayout(transition)
+    }
+
+    private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+        desktopModeWindowDecorViewModel.onTaskOpening(
+                task,
+                leash,
+                SurfaceControl.Transaction(),
+                SurfaceControl.Transaction()
+        )
+    }
+
+    private fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+        desktopModeWindowDecorViewModel.onTaskChanging(
+                task,
+                leash,
+                SurfaceControl.Transaction(),
+                SurfaceControl.Transaction()
+        )
+    }
+
+    private fun createTask(
+            displayId: Int = DEFAULT_DISPLAY,
+            @WindowConfiguration.WindowingMode windowingMode: Int,
+            activityType: Int = ACTIVITY_TYPE_STANDARD,
+            focused: Boolean = true
+    ): RunningTaskInfo {
+        return TestRunningTaskInfoBuilder()
+                .setDisplayId(displayId)
+                .setWindowingMode(windowingMode)
+                .setVisible(true)
+                .setActivityType(activityType)
+                .build().apply {
+                    isFocused = focused
+                }
+    }
+
+    private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration {
+        val decoration = mock(DesktopModeWindowDecoration::class.java)
+        whenever(mockDesktopModeWindowDecorFactory.create(
+                any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+        ).thenReturn(decoration)
+        return decoration
+    }
+
+    private fun setUpMockDecorationsForTasks(vararg tasks: RunningTaskInfo) {
+        tasks.forEach { setUpMockDecorationForTask(it) }
+    }
+
+    private fun createVirtualDisplay(): VirtualDisplay? {
+        val surfaceView = SurfaceView(mContext)
+        return mContext.getSystemService<DisplayManager>()?.createVirtualDisplay(
+                "testEventReceiversOnMultipleDisplays",
+                /*width=*/ 400,
+                /*height=*/ 400,
+                /*densityDpi=*/320,
+                surfaceView.holder.surface,
+                DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+        )
+    }
+
+    private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) {
+        configuration.windowConfiguration.windowingMode = mode
+    }
+
+    private fun RunningTaskInfo.setActivityType(type: Int) {
+        configuration.windowConfiguration.activityType = type
+    }
+
+    companion object {
+        private const val TAG = "DesktopModeWindowDecorViewModelTests"
+        private val STABLE_INSETS = Rect(0, 100, 0, 0)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
new file mode 100644
index 0000000..a2dbab1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -0,0 +1,132 @@
+/*
+ * 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.windowdecor;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.function.Supplier;
+
+/**
+ * Tests for {@link DesktopModeWindowDecoration}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DesktopModeWindowDecorationTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DesktopModeWindowDecorationTests extends ShellTestCase {
+    @Mock
+    private DisplayController mMockDisplayController;
+    @Mock
+    private ShellTaskOrganizer mMockShellTaskOrganizer;
+    @Mock
+    private Handler mMockHandler;
+    @Mock
+    private Choreographer mMockChoreographer;
+    @Mock
+    private SyncTransactionQueue mMockSyncQueue;
+    @Mock
+    private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
+    @Mock
+    private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
+    @Mock
+    private SurfaceControl.Transaction mMockTransaction;
+    @Mock
+    private SurfaceControl mMockSurfaceControl;
+    @Mock
+    private SurfaceControlViewHost mMockSurfaceControlViewHost;
+    @Mock
+    private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+
+    private final Configuration mConfiguration = new Configuration();
+
+    @Before
+    public void setUp() {
+        doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
+                any(), any(), any());
+        doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+    }
+
+    @Test
+    public void testMenusClosedWhenTaskIsInvisible() {
+        doReturn(mMockTransaction).when(mMockTransaction).hide(any());
+
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(false /* visible */);
+        final DesktopModeWindowDecoration spyWindowDecor =
+                spy(createWindowDecoration(taskInfo));
+
+        spyWindowDecor.relayout(taskInfo);
+
+        // Menus should close if open before the task being invisible causes relayout to return.
+        verify(spyWindowDecor).closeHandleMenu();
+        verify(spyWindowDecor).closeMaximizeMenu();
+
+    }
+
+    private DesktopModeWindowDecoration createWindowDecoration(
+            ActivityManager.RunningTaskInfo taskInfo) {
+        return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
+                mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mConfiguration,
+                mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
+                SurfaceControl.Builder::new, mMockTransactionSupplier,
+                WindowContainerTransaction::new, mMockSurfaceControlViewHostFactory);
+    }
+
+    private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder();
+        ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setVisible(visible)
+                .build();
+        taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor",
+                "DesktopModeWindowDecorationTests");
+        taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
+                "DesktopModeWindowDecorationTests");
+        return taskInfo;
+
+    }
+}
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index 436f107..ef4cc46 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -1,5 +1,4 @@
 set noparent
-toddke@google.com
 zyy@google.com
 patb@google.com
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 502ff87..e1dd145 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -36,11 +36,6 @@
     ],
 }
 
-java_aconfig_library {
-    name: "hwui_flags_java_lib",
-    aconfig_declarations: "hwui_flags",
-}
-
 cc_aconfig_library {
     name: "hwui_flags_cc_lib",
     aconfig_declarations: "hwui_flags",
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index d074a90..d0d3c5e 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -5,4 +5,18 @@
   namespace: "core_graphics"
   description: "API to enable apps to restrict the amount of HDR headroom that is used"
   bug: "234181960"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "hdr_10bit_plus"
+  namespace: "core_graphics"
+  description: "Use 10101010 and FP16 formats for HDR-UI when available"
+  bug: "284159488"
+}
+
+flag {
+  name: "gainmap_animations"
+  namespace: "core_graphics"
+  description: "APIs to help enable animations involving gainmaps"
+  bug: "296482289"
+}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 735fc07..30d4612 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -169,7 +169,8 @@
         return;
     }
     mGrContext->flushAndSubmit();
-    mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+    mGrContext->performDeferredCleanup(std::chrono::seconds(30),
+                                       GrPurgeResourceOptions::kAllResources);
 }
 
 void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d54c5f5..fe611a2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -123,7 +123,7 @@
         , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
         , mContentDrawBounds(0, 0, 0, 0)
         , mRenderPipeline(std::move(renderPipeline))
-        , mHintSessionWrapper(uiThreadId, renderThreadId) {
+        , mHintSessionWrapper(std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId)) {
     mRenderThread.cacheManager().registerCanvasContext(this);
     mRenderThread.renderState().registerContextCallback(this);
     rootRenderNode->makeRoot();
@@ -162,6 +162,7 @@
     destroyHardwareResources();
     mAnimationContext->destroy();
     mRenderThread.cacheManager().onContextStopped(this);
+    mHintSessionWrapper->delayedDestroy(mRenderThread, 2_s, mHintSessionWrapper);
 }
 
 static void setBufferCount(ANativeWindow* window) {
@@ -766,7 +767,7 @@
     int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
     int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
 
-    mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+    mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
 
     if (didDraw) {
         int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
@@ -774,7 +775,7 @@
         int64_t actualDuration = frameDuration -
                                  (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
                                  dequeueBufferDuration - idleDuration;
-        mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+        mHintSessionWrapper->reportActualWorkDuration(actualDuration);
     }
 
     mLastDequeueBufferDuration = dequeueBufferDuration;
@@ -1112,11 +1113,11 @@
 }
 
 void CanvasContext::sendLoadResetHint() {
-    mHintSessionWrapper.sendLoadResetHint();
+    mHintSessionWrapper->sendLoadResetHint();
 }
 
 void CanvasContext::sendLoadIncreaseHint() {
-    mHintSessionWrapper.sendLoadIncreaseHint();
+    mHintSessionWrapper->sendLoadIncreaseHint();
 }
 
 void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
@@ -1124,7 +1125,7 @@
 }
 
 void CanvasContext::startHintSession() {
-    mHintSessionWrapper.init();
+    mHintSessionWrapper->init();
 }
 
 bool CanvasContext::shouldDither() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 241f8dd..37e4f7ec 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -363,7 +363,7 @@
     std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
     std::function<void()> mPrepareSurfaceControlForWebviewCallback;
 
-    HintSessionWrapper mHintSessionWrapper;
+    std::shared_ptr<HintSessionWrapper> mHintSessionWrapper;
     nsecs_t mLastDequeueBufferDuration = 0;
     nsecs_t mSyncDelayDuration = 0;
     nsecs_t mIdleDuration = 0;
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index b34da51..d1ebe6d 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -24,6 +24,7 @@
 #include <vector>
 
 #include "../Properties.h"
+#include "RenderThread.h"
 #include "thread/CommonPool.h"
 
 using namespace std::chrono_literals;
@@ -62,8 +63,9 @@
 }
 
 void HintSessionWrapper::destroy() {
-    if (mHintSessionFuture.valid()) {
-        mHintSession = mHintSessionFuture.get();
+    if (mHintSessionFuture.has_value()) {
+        mHintSession = mHintSessionFuture->get();
+        mHintSessionFuture = std::nullopt;
     }
     if (mHintSession) {
         mBinding->closeSession(mHintSession);
@@ -74,12 +76,12 @@
 
 bool HintSessionWrapper::init() {
     if (mHintSession != nullptr) return true;
-
     // If we're waiting for the session
-    if (mHintSessionFuture.valid()) {
+    if (mHintSessionFuture.has_value()) {
         // If the session is here
-        if (mHintSessionFuture.wait_for(0s) == std::future_status::ready) {
-            mHintSession = mHintSessionFuture.get();
+        if (mHintSessionFuture->wait_for(0s) == std::future_status::ready) {
+            mHintSession = mHintSessionFuture->get();
+            mHintSessionFuture = std::nullopt;
             if (mHintSession != nullptr) {
                 mSessionValid = true;
                 return true;
@@ -136,6 +138,7 @@
         actualDurationNanos < kSanityCheckUpperBound) {
         mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
     }
+    mLastFrameNotification = systemTime();
 }
 
 void HintSessionWrapper::sendLoadResetHint() {
@@ -153,6 +156,28 @@
 void HintSessionWrapper::sendLoadIncreaseHint() {
     if (!init()) return;
     mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
+    mLastFrameNotification = systemTime();
+}
+
+bool HintSessionWrapper::alive() {
+    return mHintSession != nullptr;
+}
+
+nsecs_t HintSessionWrapper::getLastUpdate() {
+    return mLastFrameNotification;
+}
+
+// Requires passing in its shared_ptr since it shouldn't own a shared_ptr to itself
+void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
+                                        std::shared_ptr<HintSessionWrapper> wrapperPtr) {
+    nsecs_t lastUpdate = wrapperPtr->getLastUpdate();
+    rt.queue().postDelayed(delay, [lastUpdate = lastUpdate, wrapper = wrapperPtr]() mutable {
+        if (wrapper->getLastUpdate() == lastUpdate) {
+            wrapper->destroy();
+        }
+        // Ensure the shared_ptr is killed at the end of the method
+        wrapper = nullptr;
+    });
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index f8b876e..36e91ea 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -19,6 +19,7 @@
 #include <android/performance_hint.h>
 
 #include <future>
+#include <optional>
 
 #include "utils/TimeUtils.h"
 
@@ -27,6 +28,8 @@
 
 namespace renderthread {
 
+class RenderThread;
+
 class HintSessionWrapper {
 public:
     friend class HintSessionWrapperTests;
@@ -40,10 +43,15 @@
     void sendLoadIncreaseHint();
     bool init();
     void destroy();
+    bool alive();
+    nsecs_t getLastUpdate();
+    void delayedDestroy(renderthread::RenderThread& rt, nsecs_t delay,
+                        std::shared_ptr<HintSessionWrapper> wrapperPtr);
 
 private:
     APerformanceHintSession* mHintSession = nullptr;
-    std::future<APerformanceHintSession*> mHintSessionFuture;
+    // This needs to work concurrently for testing
+    std::optional<std::shared_future<APerformanceHintSession*>> mHintSessionFuture;
 
     int mResetsSinceLastReport = 0;
     nsecs_t mLastFrameNotification = 0;
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
index 623be1e..a14ae1c 100644
--- a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -23,9 +23,11 @@
 #include <chrono>
 
 #include "Properties.h"
+#include "tests/common/TestUtils.h"
 
 using namespace testing;
 using namespace std::chrono_literals;
+using namespace android::uirenderer::renderthread;
 
 APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123);
 APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456);
@@ -42,6 +44,9 @@
 protected:
     std::shared_ptr<HintSessionWrapper> mWrapper;
 
+    std::promise<int> blockDestroyCallUntil;
+    std::promise<int> waitForDestroyFinished;
+
     class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
     public:
         void init() override;
@@ -53,11 +58,17 @@
         MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t));
         MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t));
         MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t));
+        // Needs to be on the binding so it can be accessed from static methods
+        std::promise<int> allowCreationToFinish;
     };
 
     // Must be static so it can have function pointers we can point to with static methods
     static std::shared_ptr<MockHintSessionBinding> sMockBinding;
 
+    static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
+    void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
+    void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
+
     // Must be static so we can point to them as normal fn pointers with HintSessionBinding
     static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
     static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
@@ -65,6 +76,12 @@
                                                       int64_t initialTarget) {
         return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
     }
+    static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager,
+                                                             const int32_t* ids, size_t idsSize,
+                                                             int64_t initialTarget) {
+        sMockBinding->allowCreationToFinish.get_future().wait();
+        return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+    }
     static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
                                                           const int32_t* ids, size_t idsSize,
                                                           int64_t initialTarget) {
@@ -85,7 +102,21 @@
     static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
         sMockBinding->fakeSendHint(session, hintId);
     };
-    void waitForWrapperReady() { mWrapper->mHintSessionFuture.wait(); }
+    void waitForWrapperReady() {
+        if (mWrapper->mHintSessionFuture.has_value()) {
+            mWrapper->mHintSessionFuture->wait();
+        }
+    }
+    void scheduleDelayedDestroyManaged() {
+        TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) {
+            // Guaranteed to be scheduled first, allows destruction to start
+            rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); });
+            // Guaranteed to be scheduled second, destroys the session
+            mWrapper->delayedDestroy(rt, 1_ms, mWrapper);
+            // This is guaranteed to be queued after the destroy, signals that destruction is done
+            rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); });
+        });
+    }
 };
 
 std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
@@ -113,6 +144,7 @@
 }
 
 void HintSessionWrapperTests::TearDown() {
+    // Ensure that anything running on RT is completely finished
     mWrapper = nullptr;
     sMockBinding = nullptr;
 }
@@ -122,6 +154,7 @@
     sMockBinding->createSession = stubSlowCreateSession;
     mWrapper->init();
     mWrapper = nullptr;
+    Mock::VerifyAndClearExpectations(sMockBinding.get());
 }
 
 TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
@@ -148,4 +181,107 @@
     mWrapper->sendLoadResetHint();
 }
 
+TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) {
+    EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+    mWrapper->init();
+    waitForWrapperReady();
+    // Init a second time just to ensure the wrapper grabs the promise value
+    mWrapper->init();
+
+    EXPECT_EQ(mWrapper->alive(), true);
+
+    // Schedule delayed destruction, allow it to run, and check when it's done
+    scheduleDelayedDestroyManaged();
+    allowDelayedDestructionToStart();
+    waitForDelayedDestructionToFinish();
+
+    // Ensure it closed within the timeframe of the test
+    Mock::VerifyAndClearExpectations(sMockBinding.get());
+    EXPECT_EQ(mWrapper->alive(), false);
+    // If we then delete the wrapper, it shouldn't close the session again
+    EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0);
+    mWrapper = nullptr;
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) {
+    // Here we test whether queueing delayedDestroy works while creation is still happening, if
+    // creation happens after
+    EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+    sMockBinding->createSession = &stubManagedCreateSession;
+
+    // Start creating the session and destroying it at the same time
+    mWrapper->init();
+    scheduleDelayedDestroyManaged();
+
+    // Allow destruction to happen first
+    allowDelayedDestructionToStart();
+
+    // Make sure destruction has had time to happen
+    std::this_thread::sleep_for(50ms);
+
+    // Then, allow creation to finish after delayed destroy runs
+    allowCreationToFinish();
+
+    // Wait for destruction to finish
+    waitForDelayedDestructionToFinish();
+
+    // Ensure it closed within the timeframe of the test
+    Mock::VerifyAndClearExpectations(sMockBinding.get());
+    EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) {
+    // Here we test whether queueing delayedDestroy works while creation is still happening, if
+    // creation happens before
+    EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+    sMockBinding->createSession = &stubManagedCreateSession;
+
+    // Start creating the session and destroying it at the same time
+    mWrapper->init();
+    scheduleDelayedDestroyManaged();
+
+    // Allow creation to happen first
+    allowCreationToFinish();
+
+    // Make sure creation has had time to happen
+    waitForWrapperReady();
+
+    // Then allow destruction to happen after creation is done
+    allowDelayedDestructionToStart();
+
+    // Wait for it to finish
+    waitForDelayedDestructionToFinish();
+
+    // Ensure it closed within the timeframe of the test
+    Mock::VerifyAndClearExpectations(sMockBinding.get());
+    EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
+    EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
+    EXPECT_CALL(*sMockBinding,
+                fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
+            .Times(1);
+
+    mWrapper->init();
+    waitForWrapperReady();
+    // Init a second time just to grab the wrapper from the promise
+    mWrapper->init();
+    EXPECT_EQ(mWrapper->alive(), true);
+
+    // First schedule the deletion
+    scheduleDelayedDestroyManaged();
+
+    // Then, send a hint to update the timestamp
+    mWrapper->sendLoadIncreaseHint();
+
+    // Then, run the delayed deletion after sending the update
+    allowDelayedDestructionToStart();
+    waitForDelayedDestructionToFinish();
+
+    // Ensure it didn't close within the timeframe of the test
+    Mock::VerifyAndClearExpectations(sMockBinding.get());
+    EXPECT_EQ(mWrapper->alive(), true);
+}
+
 }  // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 86e53f5..83d2b61 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -13,3 +13,10 @@
     description: "Gates whether to adjust local stream volume when the app in the foreground is the last app to play audio or adjust the volume of the last active media session that the user interacted with."
     bug: "275185436"
 }
+
+flag {
+     namespace: "media_solutions"
+     name: "enable_audio_policies_device_and_bluetooth_controller"
+     description: "Use Audio Policies implementation for device and Bluetooth route controllers."
+     bug: "280576228"
+}
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 3b70890..f9eaabd 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -25,6 +25,8 @@
 import android.hardware.tv.tuner.LnbTone;
 import android.hardware.tv.tuner.LnbVoltage;
 import android.media.tv.tuner.Tuner.Result;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -147,10 +149,13 @@
     public static final int EVENT_TYPE_LNB_OVERLOAD = LnbEventType.LNB_OVERLOAD;
 
     private static final String TAG = "Lnb";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     Map<LnbCallback, Executor> mCallbackMap =
             new HashMap<LnbCallback, Executor>();
     Tuner mOwner;
+    TunerResourceManager mTunerResourceManager;
+    int mClientId;
     private final Object mCallbackLock = new Object();
 
 
@@ -174,6 +179,10 @@
             }
         }
         setOwner(tuner);
+        if (mOwner != null) {
+            mTunerResourceManager = mOwner.getTunerResourceManager();
+            mClientId = mOwner.getClientId();
+        }
     }
 
     /**
@@ -210,6 +219,8 @@
         Objects.requireNonNull(newOwner, "newOwner must not be null");
         synchronized (mLock) {
             mOwner = newOwner;
+            mTunerResourceManager = newOwner.getTunerResourceManager();
+            mClientId = newOwner.getClientId();
         }
     }
 
@@ -317,21 +328,42 @@
      * Releases the LNB instance.
      */
     public void close() {
-        synchronized (mLock) {
-            if (mIsClosed) {
-                return;
-            }
-            int res = nativeClose();
-            if (res != Tuner.RESULT_SUCCESS) {
-                TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
-            } else {
-                mIsClosed = true;
-                if (mOwner != null) {
-                    mOwner.releaseLnb();
-                    mOwner = null;
+        acquireTRMSLock("close()");
+        try {
+            synchronized (mLock) {
+                if (mIsClosed) {
+                    return;
                 }
-                mCallbackMap.clear();
+                int res = nativeClose();
+                if (res != Tuner.RESULT_SUCCESS) {
+                    TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
+                } else {
+                    mIsClosed = true;
+                    if (mOwner != null) {
+                        mOwner.releaseLnb();
+                        mOwner = null;
+                    }
+                    mCallbackMap.clear();
+                }
             }
+        } finally {
+            releaseTRMSLock();
         }
     }
+
+    private void acquireTRMSLock(String functionNameForLog) {
+        if (DEBUG) {
+            Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
+                    + "for clientId:" + mClientId);
+        }
+        if (!mTunerResourceManager.acquireLock(mClientId)) {
+            Log.e(TAG, "FAILED:acquireLock() in " + functionNameForLog
+                    + " for clientId:" + mClientId + " - this can cause deadlock between"
+                    + " Tuner API calls and onReclaimResources()");
+        }
+    }
+
+    private void releaseTRMSLock() {
+        mTunerResourceManager.releaseLock(mClientId);
+    }
 }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index f87e47e..9924fae 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -903,6 +903,9 @@
         }
     }
 
+    /**
+     * Releases Lnb resource if held. TRMS lock must be acquired prior to calling this function.
+     */
     private void closeLnb() {
         mLnbLock.lock();
         try {
@@ -2806,6 +2809,10 @@
         return mClientId;
     }
 
+    /* package */ TunerResourceManager getTunerResourceManager() {
+        return mTunerResourceManager;
+    }
+
     private void acquireTRMSLock(String functionNameForLog) {
         if (DEBUG) {
             Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 233aee2..fe26dc3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -50,46 +50,3 @@
         proguard_compatibility: false,
     },
 }
-
-android_app {
-    name: "ClockworkCredentialManager",
-    defaults: ["platform_app_defaults"],
-    certificate: "platform",
-    manifest: "wear/AndroidManifest.xml",
-    srcs: ["wear/src/**/*.kt"],
-    resource_dirs: ["wear/res"],
-
-    dex_preopt: {
-        profile_guided: true,
-        profile: "wear/profile.txt.prof",
-    },
-
-    static_libs: [
-        "PlatformComposeCore",
-        "androidx.activity_activity-compose",
-        "androidx.appcompat_appcompat",
-        "androidx.compose.foundation_foundation",
-        "androidx.compose.foundation_foundation-layout",
-        "androidx.compose.material_material-icons-core",
-        "androidx.compose.material_material-icons-extended",
-        "androidx.compose.ui_ui",
-        "androidx.core_core-ktx",
-        "androidx.credentials_credentials",
-        "androidx.lifecycle_lifecycle-extensions",
-        "androidx.lifecycle_lifecycle-livedata",
-        "androidx.lifecycle_lifecycle-runtime-ktx",
-        "androidx.lifecycle_lifecycle-viewmodel-compose",
-        "androidx.wear.compose_compose-foundation",
-        "androidx.wear.compose_compose-material",
-        "kotlinx-coroutines-core",
-    ],
-
-    platform_apis: true,
-    privileged: true,
-
-    kotlincflags: ["-Xjvm-default=all"],
-
-    optimize: {
-        proguard_compatibility: false,
-    },
-}
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
index 4161601..a5ccdb6 100644
--- a/packages/CredentialManager/AndroidManifest.xml
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -17,31 +17,43 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.credentialmanager">
+          package="com.android.credentialmanager">
 
     <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+    <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />
 
     <application
-      android:allowBackup="true"
-      android:dataExtractionRules="@xml/data_extraction_rules"
-      android:fullBackupContent="@xml/backup_rules"
-      android:icon="@mipmap/ic_launcher"
-      android:label="@string/app_name"
-      android:roundIcon="@mipmap/ic_launcher_round"
-      android:supportsRtl="true"
-      android:theme="@style/Theme.CredentialSelector">
-
-    <activity
-        android:name=".CredentialSelectorActivity"
-        android:exported="true"
-        android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
-        android:launchMode="singleTop"
+        android:allowBackup="true"
+        android:dataExtractionRules="@xml/data_extraction_rules"
+        android:fullBackupContent="@xml/backup_rules"
+        android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:excludeFromRecents="true"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
         android:theme="@style/Theme.CredentialSelector">
-    </activity>
-  </application>
+
+        <activity
+            android:name=".CredentialSelectorActivity"
+            android:exported="true"
+            android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
+            android:launchMode="singleTop"
+            android:label="@string/app_name"
+            android:excludeFromRecents="true"
+            android:theme="@style/Theme.CredentialSelector">
+        </activity>
+        <service
+            android:name=".autofill.CredentialAutofillService"
+            android:exported="false"
+            android:permission="android.permission.BIND_AUTOFILL_SERVICE">
+            <meta-data
+                android:name="android.autofill"
+                android:resource="@xml/autofill_service_configuration"/>
+            <intent-filter>
+                <action android:name="android.service.autofill.AutofillService"/>
+            </intent-filter>
+        </service>
+    </application>
 
 </manifest>
diff --git a/packages/CredentialManager/horologist/Android.bp b/packages/CredentialManager/horologist/Android.bp
new file mode 100644
index 0000000..bb324bb
--- /dev/null
+++ b/packages/CredentialManager/horologist/Android.bp
@@ -0,0 +1,27 @@
+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"],
+}
+
+// TODO: ag/24733147 - Remove this project once it is imported.
+android_library {
+    name: "Horologist",
+    manifest: "AndroidManifest.xml",
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "androidx.compose.foundation_foundation",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.ui_ui",
+        "androidx.navigation_navigation-compose",
+        "androidx.lifecycle_lifecycle-extensions",
+        "androidx.lifecycle_lifecycle-runtime-ktx",
+        "androidx.lifecycle_lifecycle-viewmodel-compose",
+        "androidx.wear.compose_compose-foundation",
+        "androidx.wear.compose_compose-material",
+        "androidx.wear.compose_compose-navigation",
+    ],
+}
diff --git a/packages/CredentialManager/horologist/AndroidManifest.xml b/packages/CredentialManager/horologist/AndroidManifest.xml
new file mode 100644
index 0000000..e386ce2
--- /dev/null
+++ b/packages/CredentialManager/horologist/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2023 Google Inc.
+ *
+ * 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.google.android.horologist">
+
+    <uses-feature android:name="android.hardware.type.watch" />
+
+</manifest>
diff --git a/packages/CredentialManager/horologist/README.md b/packages/CredentialManager/horologist/README.md
new file mode 100644
index 0000000..005ad2d
--- /dev/null
+++ b/packages/CredentialManager/horologist/README.md
@@ -0,0 +1,3 @@
+This folder is to place the code from Horologist (go/horologist).
+It should be removed once Horologist is imported to the platform and the dependencies to this
+module are updated to point to the imported Horologist.
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt
new file mode 100644
index 0000000..ae77605
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/annotations/ExperimentalHorologistApi.kt
@@ -0,0 +1,23 @@
+/*
+ * 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
+ *
+ *      https://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.google.android.horologist.annotations
+
+@RequiresOptIn(
+        message = "Horologist API is experimental. The API may be changed in the future.",
+)
+@Retention(AnnotationRetention.BINARY)
+public annotation class ExperimentalHorologistApi
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt
new file mode 100644
index 0000000..c88bbd8
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnDefaults.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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:Suppress("ObjectLiteralToLambda")
+
+package com.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState.RotaryMode
+
+/**
+ * Default layouts for ScalingLazyColumnState, based on UX guidance.
+ */
+public object ScalingLazyColumnDefaults {
+    /**
+     * Layout the first item, directly under the time text.
+     * This is positioned from the top of the screen instead of the
+     * center.
+     */
+    @ExperimentalHorologistApi
+    public fun belowTimeText(
+            rotaryMode: RotaryMode = RotaryMode.Scroll,
+            firstItemIsFullWidth: Boolean = false,
+            verticalArrangement: Arrangement.Vertical =
+                    Arrangement.spacedBy(
+                            space = 4.dp,
+                            alignment = Alignment.Top,
+                    ),
+            horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+            contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+            topPaddingDp: Dp = 32.dp + (if (firstItemIsFullWidth) 20.dp else 0.dp),
+    ): ScalingLazyColumnState.Factory {
+        return object : ScalingLazyColumnState.Factory {
+            @Composable
+            override fun create(): ScalingLazyColumnState {
+                val density = LocalDensity.current
+                val configuration = LocalConfiguration.current
+
+                return remember {
+                    val screenHeightPx =
+                            with(density) { configuration.screenHeightDp.dp.roundToPx() }
+                    val topPaddingPx = with(density) { topPaddingDp.roundToPx() }
+                    val topScreenOffsetPx = screenHeightPx / 2 - topPaddingPx
+
+                    ScalingLazyColumnState(
+                            initialScrollPosition = ScalingLazyColumnState.ScrollPosition(
+                                    index = 0,
+                                    offsetPx = topScreenOffsetPx,
+                            ),
+                            anchorType = ScalingLazyListAnchorType.ItemStart,
+                            rotaryMode = rotaryMode,
+                            verticalArrangement = verticalArrangement,
+                            horizontalAlignment = horizontalAlignment,
+                            contentPadding = contentPadding,
+                    )
+                }
+            }
+        }
+    }
+
+    /**
+     * Layout the item [initialCenterIndex] at [initialCenterOffset] from the
+     * center of the screen.
+     */
+    @ExperimentalHorologistApi
+    public fun scalingLazyColumnDefaults(
+            rotaryMode: RotaryMode = RotaryMode.Scroll,
+            initialCenterIndex: Int = 1,
+            initialCenterOffset: Int = 0,
+            verticalArrangement: Arrangement.Vertical =
+                    Arrangement.spacedBy(
+                            space = 4.dp,
+                            alignment = Alignment.Top,
+                    ),
+            horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+            contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+            autoCentering: AutoCenteringParams? = AutoCenteringParams(
+                    initialCenterIndex,
+                    initialCenterOffset,
+            ),
+            anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+            hapticsEnabled: Boolean = true,
+            reverseLayout: Boolean = false,
+    ): ScalingLazyColumnState.Factory {
+        return object : ScalingLazyColumnState.Factory {
+            @Composable
+            override fun create(): ScalingLazyColumnState {
+                return remember {
+                    ScalingLazyColumnState(
+                            initialScrollPosition = ScalingLazyColumnState.ScrollPosition(
+                                    index = initialCenterIndex,
+                                    offsetPx = initialCenterOffset,
+                            ),
+                            rotaryMode = rotaryMode,
+                            verticalArrangement = verticalArrangement,
+                            horizontalAlignment = horizontalAlignment,
+                            contentPadding = contentPadding,
+                            autoCentering = autoCentering,
+                            anchorType = anchorType,
+                            hapticsEnabled = hapticsEnabled,
+                            reverseLayout = reverseLayout,
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt
new file mode 100644
index 0000000..3a12b9f
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScalingLazyColumnState.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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:Suppress("ObjectLiteralToLambda")
+@file:OptIn(ExperimentalHorologistApi::class, ExperimentalWearFoundationApi::class)
+
+package com.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.ScalingParams
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState.RotaryMode
+import com.google.android.horologist.compose.rotaryinput.rememberDisabledHaptic
+import com.google.android.horologist.compose.rotaryinput.rememberRotaryHapticHandler
+import com.google.android.horologist.compose.rotaryinput.rotaryWithScroll
+import com.google.android.horologist.compose.rotaryinput.rotaryWithSnap
+import com.google.android.horologist.compose.rotaryinput.toRotaryScrollAdapter
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults as WearScalingLazyColumnDefaults
+
+/**
+ * A Config and State object wrapping up all configuration for a [ScalingLazyColumn].
+ * This allows defaults such as [ScalingLazyColumnDefaults.belowTimeText].
+ */
+@ExperimentalHorologistApi
+public class ScalingLazyColumnState(
+        public val initialScrollPosition: ScrollPosition = ScrollPosition(1, 0),
+        public val autoCentering: AutoCenteringParams? = AutoCenteringParams(
+                initialScrollPosition.index,
+                initialScrollPosition.offsetPx,
+        ),
+        public val anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+        public val contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+        public val rotaryMode: RotaryMode = RotaryMode.Scroll,
+        public val reverseLayout: Boolean = false,
+        public val verticalArrangement: Arrangement.Vertical =
+                Arrangement.spacedBy(
+                        space = 4.dp,
+                        alignment = if (!reverseLayout) Alignment.Top else Alignment.Bottom,
+                ),
+        public val horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+        public val flingBehavior: FlingBehavior? = null,
+        public val userScrollEnabled: Boolean = true,
+        public val scalingParams: ScalingParams = WearScalingLazyColumnDefaults.scalingParams(),
+        public val hapticsEnabled: Boolean = true,
+) {
+    private var _state: ScalingLazyListState? = null
+    public var state: ScalingLazyListState
+        get() {
+            if (_state == null) {
+                _state = ScalingLazyListState(
+                        initialScrollPosition.index,
+                        initialScrollPosition.offsetPx,
+                )
+            }
+            return _state!!
+        }
+        set(value) {
+            _state = value
+        }
+
+    public sealed interface RotaryMode {
+        public object Snap : RotaryMode
+        public object Scroll : RotaryMode
+
+        @Deprecated(
+                "Use RotaryMode.Scroll instead",
+                replaceWith = ReplaceWith("RotaryMode.Scroll"),
+        )
+        public object Fling : RotaryMode
+    }
+
+    public data class ScrollPosition(
+            val index: Int,
+            val offsetPx: Int,
+    )
+
+    public fun interface Factory {
+        @Composable
+        public fun create(): ScalingLazyColumnState
+    }
+}
+
+@Composable
+public fun rememberColumnState(
+        factory: ScalingLazyColumnState.Factory = ScalingLazyColumnDefaults.belowTimeText(),
+): ScalingLazyColumnState {
+    val columnState = factory.create()
+
+    columnState.state = rememberSaveable(saver = ScalingLazyListState.Saver) {
+        columnState.state
+    }
+
+    return columnState
+}
+
+@ExperimentalHorologistApi
+@Composable
+public fun ScalingLazyColumn(
+        columnState: ScalingLazyColumnState,
+        modifier: Modifier = Modifier,
+        content: ScalingLazyListScope.() -> Unit,
+) {
+    val focusRequester = rememberActiveFocusRequester()
+
+    val rotaryHaptics = if (columnState.hapticsEnabled) {
+        rememberRotaryHapticHandler(columnState.state)
+    } else {
+        rememberDisabledHaptic()
+    }
+    val modifierWithRotary = when (columnState.rotaryMode) {
+        RotaryMode.Snap -> modifier.rotaryWithSnap(
+                focusRequester = focusRequester,
+                rotaryScrollAdapter = columnState.state.toRotaryScrollAdapter(),
+                reverseDirection = columnState.reverseLayout,
+                rotaryHaptics = rotaryHaptics,
+        )
+
+        else -> modifier.rotaryWithScroll(
+                focusRequester = focusRequester,
+                scrollableState = columnState.state,
+                reverseDirection = columnState.reverseLayout,
+                rotaryHaptics = rotaryHaptics,
+        )
+    }
+
+    ScalingLazyColumn(
+            modifier = modifierWithRotary,
+            state = columnState.state,
+            contentPadding = columnState.contentPadding,
+            reverseLayout = columnState.reverseLayout,
+            verticalArrangement = columnState.verticalArrangement,
+            horizontalAlignment = columnState.horizontalAlignment,
+            flingBehavior = columnState.flingBehavior ?: ScrollableDefaults.flingBehavior(),
+            userScrollEnabled = columnState.userScrollEnabled,
+            scalingParams = columnState.scalingParams,
+            anchorType = columnState.anchorType,
+            autoCentering = columnState.autoCentering,
+            content = content,
+    )
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt
new file mode 100644
index 0000000..623ae1a
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/layout/ScrollAway.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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.google.android.horologist.compose.layout
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.scrollAway
+import com.google.android.horologist.compose.navscaffold.ScalingLazyColumnScrollableState
+
+internal fun Modifier.scrollAway(
+        scrollState: State<ScrollableState?>,
+): Modifier = composed {
+    when (val state = scrollState.value) {
+        is ScalingLazyColumnScrollableState -> {
+            val offsetDp = with(LocalDensity.current) {
+                state.initialOffsetPx.toDp()
+            }
+            this.scrollAway(state.scalingLazyListState, state.initialIndex, offsetDp)
+        }
+        is ScalingLazyListState -> this.scrollAway(state)
+        is LazyListState -> this.scrollAway(state)
+        is ScrollState -> this.scrollAway(state)
+        // Disabled
+        null -> this.hidden()
+        // Enabled but no scroll state
+        else -> this
+    }
+}
+
+internal fun Modifier.hidden(): Modifier = layout { _, _ -> layout(0, 0) {} }
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt
new file mode 100644
index 0000000..14c0ba1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/NavScaffoldViewModel.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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(ExperimentalHorologistApi::class, SavedStateHandleSaveableApi::class)
+
+package com.google.android.horologist.compose.navscaffold
+
+import android.os.Bundle
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
+import androidx.lifecycle.viewmodel.compose.saveable
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.material.VignettePosition
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.PositionIndicatorMode
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.TimeTextMode
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.TimeTextMode.ScrollAway
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.Off
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.On
+import com.google.android.horologist.compose.navscaffold.NavScaffoldViewModel.VignetteMode.WhenScrollable
+
+/**
+ * A ViewModel that backs the WearNavScaffold to allow each composable to interact and effect
+ * the [Scaffold] positionIndicator, vignette and timeText.
+ *
+ * A ViewModel is used to allow the same current instance to be shared between the WearNavScaffold
+ * and the composable screen via [NavHostController.currentBackStackEntry].
+ */
+public open class NavScaffoldViewModel(
+        private val savedStateHandle: SavedStateHandle,
+) : ViewModel() {
+    internal var initialIndex: Int? = null
+    internal var initialOffsetPx: Int? = null
+    internal var scrollType by mutableStateOf<ScrollType?>(null)
+
+    private lateinit var _scrollableState: ScrollableState
+
+    /**
+     * Returns the scrollable state for this composable or null if the scaffold should
+     * not consider this element to be scrollable.
+     */
+    public val scrollableState: ScrollableState?
+        get() = if (scrollType == null || scrollType == ScrollType.None) {
+            null
+        } else {
+            _scrollableState
+        }
+
+    /**
+     * The configuration of [Vignette], [WhenScrollable], [Off], [On] and if so whether top and
+     * bottom. Defaults to on for scrollable screens.
+     */
+    public var vignettePosition: VignetteMode by mutableStateOf(WhenScrollable)
+
+    /**
+     * The configuration of [TimeText], defaults to [TimeTextMode.ScrollAway] which will move the
+     * time text above the screen to avoid overlapping with the content moving up.
+     */
+    public var timeTextMode: TimeTextMode by mutableStateOf(ScrollAway)
+
+    /**
+     * The configuration of [PositionIndicator].  The default is to show a scroll bar while the
+     * scroll is in progress.
+     */
+    public var positionIndicatorMode: PositionIndicatorMode
+            by mutableStateOf(PositionIndicatorMode.On)
+
+    internal fun initializeScrollState(scrollStateBuilder: () -> ScrollState): ScrollState {
+        check(scrollType == null || scrollType == ScrollType.ScrollState)
+
+        if (scrollType == null) {
+            scrollType = ScrollType.ScrollState
+
+            _scrollableState = savedStateHandle.saveable(
+                    key = "navScaffold.ScrollState",
+                    saver = ScrollState.Saver,
+            ) {
+                scrollStateBuilder()
+            }
+        }
+
+        return _scrollableState as ScrollState
+    }
+
+    internal fun initializeScalingLazyListState(
+            scrollableStateBuilder: () -> ScalingLazyListState,
+    ): ScalingLazyListState {
+        check(scrollType == null || scrollType == ScrollType.ScalingLazyColumn)
+
+        if (scrollType == null) {
+            scrollType = ScrollType.ScalingLazyColumn
+
+            _scrollableState = savedStateHandle.saveable(
+                    key = "navScaffold.ScalingLazyListState",
+                    saver = ScalingLazyListState.Saver,
+            ) {
+                scrollableStateBuilder().also {
+                    initialIndex = it.centerItemIndex
+                    initialOffsetPx = it.centerItemScrollOffset
+                }
+            }
+        }
+
+        return _scrollableState as ScalingLazyListState
+    }
+
+    internal fun initializeScalingLazyListState(
+            columnState: ScalingLazyColumnState,
+    ) {
+        check(scrollType == null || scrollType == ScrollType.ScalingLazyColumn)
+
+        if (scrollType == null) {
+            scrollType = ScrollType.ScalingLazyColumn
+
+            initialIndex = columnState.initialScrollPosition.index
+            initialOffsetPx = columnState.initialScrollPosition.offsetPx
+
+            _scrollableState = savedStateHandle.saveable(
+                    key = "navScaffold.ScalingLazyListState",
+                    saver = ScalingLazyListState.Saver,
+            ) {
+                columnState.state
+            }
+        }
+
+        columnState.state = _scrollableState as ScalingLazyListState
+    }
+
+    internal fun initializeLazyList(
+            scrollableStateBuilder: () -> LazyListState,
+    ): LazyListState {
+        check(scrollType == null || scrollType == ScrollType.LazyList)
+
+        if (scrollType == null) {
+            scrollType = ScrollType.LazyList
+
+            _scrollableState = savedStateHandle.saveable(
+                    key = "navScaffold.LazyListState",
+                    saver = LazyListState.Saver,
+            ) {
+                scrollableStateBuilder()
+            }
+        }
+
+        return _scrollableState as LazyListState
+    }
+
+    internal enum class ScrollType {
+        None, ScalingLazyColumn, ScrollState, LazyList
+    }
+
+    /**
+     * The configuration of [TimeText], defaults to [ScrollAway] which will move the time text above the
+     * screen to avoid overlapping with the content moving up.
+     */
+    public enum class TimeTextMode {
+        On, Off, ScrollAway
+    }
+
+    /**
+     * The configuration of [PositionIndicator].  The default is to show a scroll bar while the
+     * scroll is in progress.
+     */
+    public enum class PositionIndicatorMode {
+        On, Off
+    }
+
+    /**
+     * The configuration of [Vignette], [WhenScrollable], [Off], [On] and if so whether top and
+     * bottom. Defaults to on for scrollable screens.
+     */
+    public sealed interface VignetteMode {
+        public object WhenScrollable : VignetteMode
+        public object Off : VignetteMode
+        public data class On(val position: VignettePosition) : VignetteMode
+    }
+
+    internal fun timeTextScrollableState(): ScrollableState? {
+        return when (timeTextMode) {
+            ScrollAway -> {
+                when (this.scrollType) {
+                    ScrollType.ScrollState -> {
+                        this.scrollableState as ScrollState
+                    }
+
+                    ScrollType.ScalingLazyColumn -> {
+                        val scalingLazyListState =
+                                this.scrollableState as ScalingLazyListState
+
+                        ScalingLazyColumnScrollableState(scalingLazyListState, initialIndex
+                                ?: 1, initialOffsetPx ?: 0)
+                    }
+
+                    ScrollType.LazyList -> {
+                        this.scrollableState as LazyListState
+                    }
+
+                    else -> {
+                        ScrollState(0)
+                    }
+                }
+            }
+
+            TimeTextMode.On -> {
+                ScrollState(0)
+            }
+
+            else -> {
+                null
+            }
+        }
+    }
+}
+
+internal class ScalingLazyColumnScrollableState(
+        val scalingLazyListState: ScalingLazyListState,
+        val initialIndex: Int,
+        val initialOffsetPx: Int,
+) : ScrollableState by scalingLazyListState
+
+/**
+ * The context items provided to a navigation composable.
+ *
+ * The [viewModel] can be used to customise the scaffold behaviour.
+ */
+public data class ScaffoldContext<T : ScrollableState>(
+        val backStackEntry: NavBackStackEntry,
+        val scrollableState: T,
+        val viewModel: NavScaffoldViewModel,
+) {
+    var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+    var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+    val arguments: Bundle?
+        get() = backStackEntry.arguments
+}
+
+public data class NonScrollableScaffoldContext(
+        val backStackEntry: NavBackStackEntry,
+        val viewModel: NavScaffoldViewModel,
+) {
+    var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+    var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+    val arguments: Bundle?
+        get() = backStackEntry.arguments
+}
+
+/**
+ * The context items provided to a navigation composable.
+ *
+ * The [viewModel] can be used to customise the scaffold behaviour.
+ */
+public data class ScrollableScaffoldContext(
+        val backStackEntry: NavBackStackEntry,
+        val columnState: ScalingLazyColumnState,
+        val viewModel: NavScaffoldViewModel,
+) {
+    val scrollableState: ScalingLazyListState
+        get() = columnState.state
+
+    var timeTextMode: TimeTextMode by viewModel::timeTextMode
+
+    var positionIndicatorMode: PositionIndicatorMode by viewModel::positionIndicatorMode
+
+    val arguments: Bundle?
+        get() = backStackEntry.arguments
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt
new file mode 100644
index 0000000..315d822
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/navscaffold/WearNavScaffold.kt
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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(ExperimentalWearFoundationApi::class)
+
+package com.google.android.horologist.compose.navscaffold
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavDeepLink
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.HierarchicalFocusCoordinator
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.navigation.SwipeDismissableNavHost
+import androidx.wear.compose.navigation.SwipeDismissableNavHostState
+import androidx.wear.compose.navigation.composable
+import androidx.wear.compose.navigation.currentBackStackEntryAsState
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.scrollAway
+
+/**
+ * A Navigation and Scroll aware [Scaffold].
+ *
+ * In addition to [NavGraphBuilder.scrollable], 3 additional extensions are supported
+ * [scalingLazyColumnComposable], [scrollStateComposable] and
+ * [lazyListComposable].
+ *
+ * These should be used to build the [ScrollableState] or [FocusRequester] as well as
+ * configure the behaviour of [TimeText], [PositionIndicator] or [Vignette].
+ */
+@Composable
+public fun WearNavScaffold(
+        startDestination: String,
+        navController: NavHostController,
+        modifier: Modifier = Modifier,
+        snackbar: @Composable () -> Unit = {},
+        timeText: @Composable (Modifier) -> Unit = {
+            TimeText(
+                    modifier = it,
+            )
+        },
+        state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
+        builder: NavGraphBuilder.() -> Unit,
+) {
+    val currentBackStackEntry: NavBackStackEntry? by navController.currentBackStackEntryAsState()
+
+    val viewModel: NavScaffoldViewModel? = currentBackStackEntry?.let {
+        viewModel(viewModelStoreOwner = it)
+    }
+
+    val scrollState: State<ScrollableState?> = remember(viewModel) {
+        derivedStateOf {
+            viewModel?.timeTextScrollableState()
+        }
+    }
+
+    Scaffold(
+            modifier = modifier.fillMaxSize(),
+            timeText = {
+                timeText(Modifier.scrollAway(scrollState))
+            },
+            positionIndicator = {
+                key(currentBackStackEntry?.destination?.route) {
+                    val mode = viewModel?.positionIndicatorMode
+
+                    if (mode == NavScaffoldViewModel.PositionIndicatorMode.On) {
+                        NavPositionIndicator(viewModel)
+                    }
+                }
+            },
+            vignette = {
+                key(currentBackStackEntry?.destination?.route) {
+                    val vignettePosition = viewModel?.vignettePosition
+                    if (vignettePosition is NavScaffoldViewModel.VignetteMode.On) {
+                        Vignette(vignettePosition = vignettePosition.position)
+                    }
+                }
+            },
+    ) {
+        Box {
+            SwipeDismissableNavHost(
+                    navController = navController,
+                    startDestination = startDestination,
+                    state = state,
+            ) {
+                builder()
+            }
+
+            snackbar()
+        }
+    }
+}
+
+@Composable
+private fun NavPositionIndicator(viewModel: NavScaffoldViewModel) {
+    when (viewModel.scrollType) {
+        NavScaffoldViewModel.ScrollType.ScrollState ->
+            PositionIndicator(
+                    scrollState = viewModel.scrollableState as ScrollState,
+            )
+
+        NavScaffoldViewModel.ScrollType.ScalingLazyColumn -> {
+            PositionIndicator(
+                    scalingLazyListState = viewModel.scrollableState as ScalingLazyListState,
+            )
+        }
+
+        NavScaffoldViewModel.ScrollType.LazyList ->
+            PositionIndicator(
+                    lazyListState = viewModel.scrollableState as LazyListState,
+            )
+
+        else -> {}
+    }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a ScalingLazyColumn.
+ *
+ * The scalingLazyListState must be taken from the [ScaffoldContext].
+ */
+@Deprecated(
+        "Use listComposable",
+)
+public fun NavGraphBuilder.scalingLazyColumnComposable(
+        route: String,
+        arguments: List<NamedNavArgument> = emptyList(),
+        deepLinks: List<NavDeepLink> = emptyList(),
+        scrollStateBuilder: () -> ScalingLazyListState,
+        content: @Composable (ScaffoldContext<ScalingLazyListState>) -> Unit,
+) {
+    composable(route, arguments, deepLinks) {
+        FocusedDestination {
+            val viewModel: NavScaffoldViewModel = viewModel(it)
+
+            val scrollState = viewModel.initializeScalingLazyListState(scrollStateBuilder)
+
+            content(ScaffoldContext(it, scrollState, viewModel))
+        }
+    }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a ScalingLazyColumn.
+ *
+ * The [ScalingLazyColumnState] must be taken from the [ScrollableScaffoldContext].
+ */
+@ExperimentalHorologistApi
+public fun NavGraphBuilder.scrollable(
+        route: String,
+        arguments: List<NamedNavArgument> = emptyList(),
+        deepLinks: List<NavDeepLink> = emptyList(),
+        columnStateFactory: ScalingLazyColumnState.Factory =
+                ScalingLazyColumnDefaults.belowTimeText(),
+        content: @Composable (ScrollableScaffoldContext) -> Unit,
+) {
+    this@scrollable.composable(route, arguments, deepLinks) {
+        FocusedDestination {
+            val columnState = columnStateFactory.create()
+
+            val viewModel: NavScaffoldViewModel = viewModel(it)
+
+            viewModel.initializeScalingLazyListState(columnState)
+
+            content(ScrollableScaffoldContext(it, columnState, viewModel))
+        }
+    }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a Scrollable item.
+ *
+ * The scrollState must be taken from the [ScaffoldContext].
+ */
+public fun NavGraphBuilder.scrollStateComposable(
+        route: String,
+        arguments: List<NamedNavArgument> = emptyList(),
+        deepLinks: List<NavDeepLink> = emptyList(),
+        scrollStateBuilder: () -> ScrollState = { ScrollState(0) },
+        content: @Composable (ScaffoldContext<ScrollState>) -> Unit,
+) {
+    composable(route, arguments, deepLinks) {
+        FocusedDestination {
+            val viewModel: NavScaffoldViewModel = viewModel(it)
+
+            val scrollState = viewModel.initializeScrollState(scrollStateBuilder)
+
+            content(ScaffoldContext(it, scrollState, viewModel))
+        }
+    }
+}
+
+/**
+ * Add a screen to the navigation graph featuring a Lazy list such as LazyColumn.
+ *
+ * The scrollState must be taken from the [ScaffoldContext].
+ */
+public fun NavGraphBuilder.lazyListComposable(
+        route: String,
+        arguments: List<NamedNavArgument> = emptyList(),
+        deepLinks: List<NavDeepLink> = emptyList(),
+        lazyListStateBuilder: () -> LazyListState = { LazyListState() },
+        content: @Composable (ScaffoldContext<LazyListState>) -> Unit,
+) {
+    composable(route, arguments, deepLinks) {
+        FocusedDestination {
+            val viewModel: NavScaffoldViewModel = viewModel(it)
+
+            val scrollState = viewModel.initializeLazyList(lazyListStateBuilder)
+
+            content(ScaffoldContext(it, scrollState, viewModel))
+        }
+    }
+}
+
+/**
+ * Add non scrolling screen to the navigation graph. The [NavBackStackEntry] and
+ * [NavScaffoldViewModel] are passed into the [content] block so that
+ * the Scaffold may be customised, such as disabling TimeText.
+ */
+@Deprecated(
+        "Use composable",
+        ReplaceWith("composable(route, arguments, deepLinks, lazyListStateBuilder, content)"),
+)
+public fun NavGraphBuilder.wearNavComposable(
+        route: String,
+        arguments: List<NamedNavArgument> = emptyList(),
+        deepLinks: List<NavDeepLink> = emptyList(),
+        content: @Composable (NavBackStackEntry, NavScaffoldViewModel) -> Unit,
+) {
+    composable(route, arguments, deepLinks) {
+        FocusedDestination {
+            val viewModel: NavScaffoldViewModel = viewModel()
+
+            content(it, viewModel)
+        }
+    }
+}
+
+/**
+ * Add non scrolling screen to the navigation graph. The [NavBackStackEntry] and
+ * [NavScaffoldViewModel] are passed into the [content] block so that
+ * the Scaffold may be customised, such as disabling TimeText.
+ */
+@ExperimentalHorologistApi
+public fun NavGraphBuilder.composable(
+        route: String,
+        arguments: List<NamedNavArgument> = emptyList(),
+        deepLinks: List<NavDeepLink> = emptyList(),
+        content: @Composable (NonScrollableScaffoldContext) -> Unit,
+) {
+    this@composable.composable(route, arguments, deepLinks) {
+        FocusedDestination {
+            val viewModel: NavScaffoldViewModel = viewModel()
+
+            content(NonScrollableScaffoldContext(it, viewModel))
+        }
+    }
+}
+
+@Composable
+internal fun FocusedDestination(content: @Composable () -> Unit) {
+    val lifecycle = LocalLifecycleOwner.current.lifecycle
+    val focused =
+            remember { mutableStateOf(lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) }
+
+    DisposableEffect(lifecycle) {
+        val listener = LifecycleEventObserver { _, _ ->
+            focused.value = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
+        }
+        lifecycle.addObserver(listener)
+        onDispose {
+            lifecycle.removeObserver(listener)
+        }
+    }
+
+    HierarchicalFocusCoordinator(requiresFocus = { focused.value }) {
+        content()
+    }
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt
new file mode 100644
index 0000000..c4af4a6
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Haptics.kt
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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(ExperimentalHorologistApi::class)
+
+package com.google.android.horologist.compose.rotaryinput
+
+import android.os.Build
+import android.view.HapticFeedbackConstants
+import android.view.View
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalView
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.withContext
+import kotlin.math.abs
+
+private const val DEBUG = false
+
+/**
+ * Debug logging that can be enabled.
+ */
+private inline fun debugLog(generateMsg: () -> String) {
+    if (DEBUG) {
+        println("RotaryHaptics: ${generateMsg()}")
+    }
+}
+
+/**
+ * Throttling events within specified timeframe. Only first and last events will be received.
+ * For a flow emitting elements 1 to 30, with a 100ms delay between them:
+ * ```
+ * val flow = flow {
+ *     for (i in 1..30) {
+ *         delay(100)
+ *         emit(i)
+ *     }
+ * }
+ * ```
+ * With timeframe=1000 only those integers will be received: 1, 10, 20, 30 .
+ */
+internal fun <T> Flow<T>.throttleLatest(timeframe: Long): Flow<T> =
+        flow {
+            conflate().collect {
+                emit(it)
+                delay(timeframe)
+            }
+        }
+
+/**
+ * Handles haptics for rotary usage
+ */
+@ExperimentalHorologistApi
+public interface RotaryHapticHandler {
+
+    /**
+     * Handles haptics when scroll is used
+     */
+    @ExperimentalHorologistApi
+    public fun handleScrollHaptic(scrollDelta: Float)
+
+    /**
+     * Handles haptics when scroll with snap is used
+     */
+    @ExperimentalHorologistApi
+    public fun handleSnapHaptic(scrollDelta: Float)
+}
+
+/**
+ * Default implementation of [RotaryHapticHandler]. It handles haptic feedback based
+ * on the [scrollableState], scrolled pixels and [hapticsThresholdPx].
+ * Haptic is not fired in this class, instead it's sent to [hapticsChannel]
+ * where it'll performed later.
+ *
+ * @param scrollableState Haptic performed based on this state
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ */
+public class DefaultRotaryHapticHandler(
+        private val scrollableState: ScrollableState,
+        private val hapticsChannel: Channel<RotaryHapticsType>,
+        private val hapticsThresholdPx: Long = 50,
+) : RotaryHapticHandler {
+
+    private var overscrollHapticTriggered = false
+    private var currScrollPosition = 0f
+    private var prevHapticsPosition = 0f
+
+    override fun handleScrollHaptic(scrollDelta: Float) {
+        if ((scrollDelta > 0 && !scrollableState.canScrollForward) ||
+                (scrollDelta < 0 && !scrollableState.canScrollBackward)
+        ) {
+            if (!overscrollHapticTriggered) {
+                trySendHaptic(RotaryHapticsType.ScrollLimit)
+                overscrollHapticTriggered = true
+            }
+        } else {
+            overscrollHapticTriggered = false
+            currScrollPosition += scrollDelta
+            val diff = abs(currScrollPosition - prevHapticsPosition)
+
+            if (diff >= hapticsThresholdPx) {
+                trySendHaptic(RotaryHapticsType.ScrollTick)
+                prevHapticsPosition = currScrollPosition
+            }
+        }
+    }
+
+    override fun handleSnapHaptic(scrollDelta: Float) {
+        if ((scrollDelta > 0 && !scrollableState.canScrollForward) ||
+                (scrollDelta < 0 && !scrollableState.canScrollBackward)
+        ) {
+            if (!overscrollHapticTriggered) {
+                trySendHaptic(RotaryHapticsType.ScrollLimit)
+                overscrollHapticTriggered = true
+            }
+        } else {
+            overscrollHapticTriggered = false
+            trySendHaptic(RotaryHapticsType.ScrollItemFocus)
+        }
+    }
+
+    private fun trySendHaptic(rotaryHapticsType: RotaryHapticsType) {
+        // Ok to ignore the ChannelResult because we default to capacity = 2 and DROP_OLDEST
+        @Suppress("UNUSED_VARIABLE")
+        val unused = hapticsChannel.trySend(rotaryHapticsType)
+    }
+}
+
+/**
+ * Interface for Rotary haptic feedback
+ */
+@ExperimentalHorologistApi
+public interface RotaryHapticFeedback {
+    @ExperimentalHorologistApi
+    public fun performHapticFeedback(type: RotaryHapticsType)
+}
+
+/**
+ * Rotary haptic types
+ */
+@ExperimentalHorologistApi
+@JvmInline
+public value class RotaryHapticsType(private val type: Int) {
+    public companion object {
+        /**
+         * A scroll ticking haptic. Similar to texture haptic - performed each time when
+         * a scrollable content is scrolled by a certain distance
+         */
+        @ExperimentalHorologistApi
+        public val ScrollTick: RotaryHapticsType = RotaryHapticsType(1)
+
+        /**
+         * An item focus (snap) haptic. Performed when a scrollable content is snapped
+         * to a specific item.
+         */
+        @ExperimentalHorologistApi
+        public val ScrollItemFocus: RotaryHapticsType = RotaryHapticsType(2)
+
+        /**
+         * A limit(overscroll) haptic. Performed when a list reaches the limit
+         * (start or end) and can't scroll further
+         */
+        @ExperimentalHorologistApi
+        public val ScrollLimit: RotaryHapticsType = RotaryHapticsType(3)
+    }
+}
+
+/**
+ * Remember disabled haptics handler
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun rememberDisabledHaptic(): RotaryHapticHandler = remember {
+    object : RotaryHapticHandler {
+
+        override fun handleScrollHaptic(scrollDelta: Float) {
+            // Do nothing
+        }
+
+        override fun handleSnapHaptic(scrollDelta: Float) {
+            // Do nothing
+        }
+    }
+}
+
+/**
+ * Remember rotary haptic handler.
+ * @param scrollableState A scrollableState, used to determine whether the end of the scrollable
+ * was reached or not.
+ * @param throttleThresholdMs Throttling events within specified timeframe.
+ * Only first and last events will be received. Check [throttleLatest] for more info.
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param rotaryHaptics Interface for Rotary haptic feedback which performs haptics
+ */
+@ExperimentalHorologistApi
+@Composable
+public fun rememberRotaryHapticHandler(
+        scrollableState: ScrollableState,
+        throttleThresholdMs: Long = 30,
+        hapticsThresholdPx: Long = 50,
+        hapticsChannel: Channel<RotaryHapticsType> = rememberHapticChannel(),
+        rotaryHaptics: RotaryHapticFeedback = rememberDefaultRotaryHapticFeedback(),
+): RotaryHapticHandler {
+    return remember(scrollableState, hapticsChannel, rotaryHaptics) {
+        DefaultRotaryHapticHandler(scrollableState, hapticsChannel, hapticsThresholdPx)
+    }.apply {
+        LaunchedEffect(hapticsChannel) {
+            hapticsChannel.receiveAsFlow()
+                    .throttleLatest(throttleThresholdMs)
+                    .collect { hapticType ->
+                        // 'withContext' launches performHapticFeedback in a separate thread,
+                        // as otherwise it produces a visible lag (b/219776664)
+                        val currentTime = System.currentTimeMillis()
+                        debugLog { "Haptics started" }
+                        withContext(Dispatchers.Default) {
+                            debugLog {
+                                "Performing haptics, delay: " +
+                                        "${System.currentTimeMillis() - currentTime}"
+                            }
+                            rotaryHaptics.performHapticFeedback(hapticType)
+                        }
+                    }
+        }
+    }
+}
+
+@Composable
+private fun rememberHapticChannel() =
+        remember {
+            Channel<RotaryHapticsType>(
+                    capacity = 2,
+                    onBufferOverflow = BufferOverflow.DROP_OLDEST,
+            )
+        }
+
+@ExperimentalHorologistApi
+@Composable
+public fun rememberDefaultRotaryHapticFeedback(): RotaryHapticFeedback =
+        LocalView.current.let { view -> remember { findDeviceSpecificHapticFeedback(view) } }
+
+internal fun findDeviceSpecificHapticFeedback(view: View): RotaryHapticFeedback =
+        if (isGooglePixelWatch()) {
+            PixelWatchRotaryHapticFeedback(view)
+        } else if (isGalaxyWatchClassic()) {
+            GalaxyWatchClassicHapticFeedback(view)
+        } else {
+            DefaultRotaryHapticFeedback(view)
+        }
+
+/**
+ * Default Rotary implementation for [RotaryHapticFeedback]
+ */
+@ExperimentalHorologistApi
+public class DefaultRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+    @ExperimentalHorologistApi
+    override fun performHapticFeedback(
+            type: RotaryHapticsType,
+    ) {
+        when (type) {
+            RotaryHapticsType.ScrollItemFocus -> {
+                view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+            }
+
+            RotaryHapticsType.ScrollTick -> {
+                view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
+            }
+
+            RotaryHapticsType.ScrollLimit -> {
+                view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+            }
+        }
+    }
+}
+
+/**
+ * Implementation of [RotaryHapticFeedback] for Pixel Watch
+ */
+@ExperimentalHorologistApi
+private class PixelWatchRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+    @ExperimentalHorologistApi
+    override fun performHapticFeedback(
+            type: RotaryHapticsType,
+    ) {
+        when (type) {
+            RotaryHapticsType.ScrollItemFocus -> {
+                view.performHapticFeedback(
+                        if (Build.VERSION.SDK_INT >= 33) {
+                            ROTARY_SCROLL_ITEM_FOCUS
+                        } else {
+                            WEAR_SCROLL_ITEM_FOCUS
+                        },
+                )
+            }
+
+            RotaryHapticsType.ScrollTick -> {
+                view.performHapticFeedback(
+                        if (Build.VERSION.SDK_INT >= 33) ROTARY_SCROLL_TICK else WEAR_SCROLL_TICK,
+                )
+            }
+
+            RotaryHapticsType.ScrollLimit -> {
+                view.performHapticFeedback(
+                        if (Build.VERSION.SDK_INT >= 33) ROTARY_SCROLL_LIMIT else WEAR_SCROLL_LIMIT,
+                )
+            }
+        }
+    }
+
+    private companion object {
+        // Hidden constants from HapticFeedbackConstants.java specific for Pixel Watch
+        // API 33
+        public const val ROTARY_SCROLL_TICK: Int = 18
+        public const val ROTARY_SCROLL_ITEM_FOCUS: Int = 19
+        public const val ROTARY_SCROLL_LIMIT: Int = 20
+
+        // API 30
+        public const val WEAR_SCROLL_TICK: Int = 10002
+        public const val WEAR_SCROLL_ITEM_FOCUS: Int = 10003
+        public const val WEAR_SCROLL_LIMIT: Int = 10003
+    }
+}
+
+/**
+ * Implementation of [RotaryHapticFeedback] for Galaxy Watch 4 Classic
+ */
+@ExperimentalHorologistApi
+private class GalaxyWatchClassicHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+    @ExperimentalHorologistApi
+    override fun performHapticFeedback(
+            type: RotaryHapticsType,
+    ) {
+        when (type) {
+            RotaryHapticsType.ScrollItemFocus -> {
+                // No haptic for scroll snap ( we have physical bezel)
+            }
+
+            RotaryHapticsType.ScrollTick -> {
+                // No haptic for scroll tick ( we have physical bezel)
+            }
+
+            RotaryHapticsType.ScrollLimit -> {
+                view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+            }
+        }
+    }
+}
+
+private fun isGalaxyWatchClassic(): Boolean =
+        Build.MODEL.matches("SM-R8[89]5.".toRegex())
+
+private fun isGooglePixelWatch(): Boolean =
+        Build.MODEL.startsWith("Google Pixel Watch")
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt
new file mode 100644
index 0000000..3ca16c1
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/Rotary.kt
@@ -0,0 +1,1301 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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(ExperimentalHorologistApi::class)
+
+package com.google.android.horologist.compose.rotaryinput
+
+import android.view.ViewConfiguration
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.animateTo
+import androidx.compose.animation.core.copy
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.rotary.onRotaryScrollEvent
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.util.fastSumBy
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.transformLatest
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+import kotlin.math.sign
+
+private const val DEBUG = false
+
+/**
+ * Debug logging that can be enabled.
+ */
+private inline fun debugLog(generateMsg: () -> String) {
+    if (DEBUG) {
+        println("RotaryScroll: ${generateMsg()}")
+    }
+}
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports fling.
+ *
+ *  Fling algorithm:
+ * - A scroll with RSB/ Bezel happens.
+ * - If this is a first rotary event after the threshold ( by default 200ms), a new scroll
+ * session starts by resetting all necessary parameters
+ * - A delta value is added into VelocityTracker and a new speed is calculated.
+ * - If the current speed is bigger than the previous one,  this value is remembered as
+ * a latest fling speed with a timestamp
+ * - After each scroll event a fling countdown starts ( by default 70ms) which
+ * resets if new scroll event is received
+ * - If fling countdown is finished - it means that the finger was probably raised from RSB, there will be no other events and probably
+ * this is the last event during this session. After it a fling is triggered.
+ * - Fling is stopped when a new scroll event happens
+ *
+ * The screen containing the scrollable item should request the focus
+ * by calling [requestFocus] method
+ *
+ * ```
+ * LaunchedEffect(Unit) {
+ *   focusRequester.requestFocus()
+ * }
+ * ```
+ * @param focusRequester Requests the focus for rotary input
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param flingBehavior Logic describing fling behavior.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Deprecated(
+        "Use rotaryWithScroll instead",
+        ReplaceWith(
+                "this.rotaryWithScroll(scrollableState, focusRequester, " +
+                        "flingBehavior, rotaryHaptics, reverseDirection)",
+        ),
+)
+@Composable
+public fun Modifier.rotaryWithFling(
+        focusRequester: FocusRequester,
+        scrollableState: ScrollableState,
+        flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
+        rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+        reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+        rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+        reverseDirection = reverseDirection,
+        rotaryHaptics = rotaryHaptics,
+)
+        .focusRequester(focusRequester)
+        .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports scroll with fling.
+ *
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param focusRequester Requests the focus for rotary input.
+ * By default comes from [rememberActiveFocusRequester],
+ * which is used with [HierarchicalFocusCoordinator]
+ * @param flingBehavior Logic describing fling behavior. If null fling will not happen.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Composable
+public fun Modifier.rotaryWithScroll(
+        scrollableState: ScrollableState,
+        focusRequester: FocusRequester = rememberActiveFocusRequester(),
+        flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
+        rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+        reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+        rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+        reverseDirection = reverseDirection,
+        rotaryHaptics = rotaryHaptics,
+)
+        .focusRequester(focusRequester)
+        .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable.
+ * This modifier supports snap.
+ *
+ * @param focusRequester Requests the focus for rotary input.
+ * By default comes from [rememberActiveFocusRequester],
+ * which is used with [HierarchicalFocusCoordinator]
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with
+ * Scrollable `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@ExperimentalHorologistApi
+@Suppress("ComposableModifierFactory")
+@Composable
+public fun Modifier.rotaryWithSnap(
+        rotaryScrollAdapter: RotaryScrollAdapter,
+        focusRequester: FocusRequester = rememberActiveFocusRequester(),
+        snapParameters: SnapParameters = RotaryDefaults.snapParametersDefault(),
+        rotaryHaptics: RotaryHapticHandler =
+                rememberRotaryHapticHandler(rotaryScrollAdapter.scrollableState),
+        reverseDirection: Boolean = false,
+): Modifier = rotaryHandler(
+        rotaryScrollHandler =
+            RotaryDefaults.rememberSnapHandler(rotaryScrollAdapter, snapParameters),
+        reverseDirection = reverseDirection,
+        rotaryHaptics = rotaryHaptics,
+)
+        .focusRequester(focusRequester)
+        .focusable()
+
+/**
+ * An extension function for creating [RotaryScrollAdapter] from [ScalingLazyListState]
+ */
+@ExperimentalHorologistApi
+public fun ScalingLazyListState.toRotaryScrollAdapter(): RotaryScrollAdapter =
+        ScalingLazyColumnRotaryScrollAdapter(this)
+
+/**
+ * An implementation of rotary scroll adapter for [ScalingLazyColumn]
+ */
+@ExperimentalHorologistApi
+public class ScalingLazyColumnRotaryScrollAdapter(
+        override val scrollableState: ScalingLazyListState,
+) : RotaryScrollAdapter {
+
+    /**
+     * Calculates an average height of an item by taking an average from visible items height.
+     */
+    override fun averageItemSize(): Float {
+        val visibleItems = scrollableState.layoutInfo.visibleItemsInfo
+        return (visibleItems.fastSumBy { it.unadjustedSize } / visibleItems.size).toFloat()
+    }
+
+    /**
+     * Current (centred) item index
+     */
+    override fun currentItemIndex(): Int = scrollableState.centerItemIndex
+
+    /**
+     * An offset from the item centre
+     */
+    override fun currentItemOffset(): Float = scrollableState.centerItemScrollOffset.toFloat()
+
+    /**
+     * The total count of items in ScalingLazyColumn
+     */
+    override fun totalItemsCount(): Int = scrollableState.layoutInfo.totalItemsCount
+}
+
+/**
+ * An adapter which connects scrollableState to Rotary
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollAdapter {
+
+    /**
+     * A scrollable state. Used for performing scroll when Rotary events received
+     */
+    @ExperimentalHorologistApi
+    public val scrollableState: ScrollableState
+
+    /**
+     * Average size of an item. Used for estimating the scrollable distance
+     */
+    @ExperimentalHorologistApi
+    public fun averageItemSize(): Float
+
+    /**
+     * A current item index. Used for scrolling
+     */
+    @ExperimentalHorologistApi
+    public fun currentItemIndex(): Int
+
+    /**
+     * An offset from the centre or the border of the current item.
+     */
+    @ExperimentalHorologistApi
+    public fun currentItemOffset(): Float
+
+    /**
+     * The total count of items in [scrollableState]
+     */
+    @ExperimentalHorologistApi
+    public fun totalItemsCount(): Int
+}
+
+/**
+ * Defaults for rotary modifiers
+ */
+@ExperimentalHorologistApi
+public object RotaryDefaults {
+
+    /**
+     * Handles scroll with fling.
+     * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+     * @param flingBehavior Logic describing Fling behavior. If null - fling will not happen
+     * @param isLowRes Whether the input is Low-res (a bezel) or high-res(a crown/rsb)
+     */
+    @ExperimentalHorologistApi
+    @Composable
+    public fun rememberFlingHandler(
+            scrollableState: ScrollableState,
+            flingBehavior: FlingBehavior? = null,
+            isLowRes: Boolean = isLowResInput(),
+    ): RotaryScrollHandler {
+        val viewConfiguration = ViewConfiguration.get(LocalContext.current)
+
+        return remember(scrollableState, flingBehavior, isLowRes) {
+            debugLog { "isLowRes : $isLowRes" }
+            fun rotaryFlingBehavior() = flingBehavior?.run {
+                DefaultRotaryFlingBehavior(
+                        scrollableState,
+                        flingBehavior,
+                        viewConfiguration,
+                        flingTimeframe =
+                            if (isLowRes) lowResFlingTimeframe else highResFlingTimeframe,
+                )
+            }
+
+            fun scrollBehavior() = AnimationScrollBehavior(scrollableState)
+
+            if (isLowRes) {
+                LowResRotaryScrollHandler(
+                        rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+                        scrollBehaviorFactory = { scrollBehavior() },
+                )
+            } else {
+                HighResRotaryScrollHandler(
+                        rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+                        scrollBehaviorFactory = { scrollBehavior() },
+                )
+            }
+        }
+    }
+
+    /**
+     * Handles scroll with snap
+     * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+     * @param snapParameters Snap parameters
+     */
+    @ExperimentalHorologistApi
+    @Composable
+    public fun rememberSnapHandler(
+            rotaryScrollAdapter: RotaryScrollAdapter,
+            snapParameters: SnapParameters = snapParametersDefault(),
+            isLowRes: Boolean = isLowResInput(),
+    ): RotaryScrollHandler {
+        return remember(rotaryScrollAdapter, snapParameters) {
+            if (isLowRes) {
+                LowResSnapHandler(
+                        snapBehaviourFactory = {
+                            DefaultSnapBehavior(rotaryScrollAdapter, snapParameters)
+                        },
+                )
+            } else {
+                HighResSnapHandler(
+                        resistanceFactor = snapParameters.resistanceFactor,
+                        thresholdBehaviorFactory = {
+                            ThresholdBehavior(
+                                    rotaryScrollAdapter,
+                                    snapParameters.thresholdDivider,
+                            )
+                        },
+                        snapBehaviourFactory = {
+                            DefaultSnapBehavior(rotaryScrollAdapter, snapParameters)
+                        },
+                        scrollBehaviourFactory = {
+                            AnimationScrollBehavior(rotaryScrollAdapter.scrollableState)
+                        },
+                )
+            }
+        }
+    }
+
+    /**
+     * Returns default [SnapParameters]
+     */
+    @ExperimentalHorologistApi
+    public fun snapParametersDefault(): SnapParameters =
+            SnapParameters(
+                    snapOffset = 0,
+                    thresholdDivider = 1.5f,
+                    resistanceFactor = 3f,
+            )
+
+    /**
+     * Returns whether the input is Low-res (a bezel) or high-res(a crown/rsb).
+     */
+    @ExperimentalHorologistApi
+    @Composable
+    public fun isLowResInput(): Boolean = LocalContext.current.packageManager
+            .hasSystemFeature("android.hardware.rotaryencoder.lowres")
+
+    private val lowResFlingTimeframe: Long = 100L
+    private val highResFlingTimeframe: Long = 30L
+}
+
+/**
+ * Parameters used for snapping
+ *
+ * @param snapOffset an optional offset to be applied when snapping the item. After the snap the
+ * snapped items offset will be [snapOffset].
+ */
+public class SnapParameters(
+        public val snapOffset: Int,
+        public val thresholdDivider: Float,
+        public val resistanceFactor: Float,
+) {
+    /**
+     * Returns a snapping offset in [Dp]
+     */
+    @Composable
+    public fun snapOffsetDp(): Dp {
+        return with(LocalDensity.current) {
+            snapOffset.toDp()
+        }
+    }
+}
+
+/**
+ * An interface for handling scroll events
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollHandler {
+    /**
+     * Handles scrolling events
+     * @param coroutineScope A scope for performing async actions
+     * @param event A scrollable event from rotary input, containing scrollable delta and timestamp
+     * @param rotaryHaptics
+     */
+    @ExperimentalHorologistApi
+    public suspend fun handleScrollEvent(
+            coroutineScope: CoroutineScope,
+            event: TimestampedDelta,
+            rotaryHaptics: RotaryHapticHandler,
+    )
+}
+
+/**
+ * An interface for scrolling behavior
+ */
+@ExperimentalHorologistApi
+public interface RotaryScrollBehavior {
+    /**
+     * Handles scroll event to [targetValue]
+     */
+    @ExperimentalHorologistApi
+    public suspend fun handleEvent(targetValue: Float)
+}
+
+/**
+ * Default implementation of [RotaryFlingBehavior]
+ */
+@ExperimentalHorologistApi
+public class DefaultRotaryFlingBehavior(
+        private val scrollableState: ScrollableState,
+        private val flingBehavior: FlingBehavior,
+        viewConfiguration: ViewConfiguration,
+        private val flingTimeframe: Long,
+) : RotaryFlingBehavior {
+
+    // A time range during which the fling is valid.
+    // For simplicity it's twice as long as [flingTimeframe]
+    private val timeRangeToFling = flingTimeframe * 2
+
+    //  A default fling factor for making fling slower
+    private val flingScaleFactor = 0.7f
+
+    private var previousVelocity = 0f
+
+    private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+    private val minFlingSpeed = viewConfiguration.scaledMinimumFlingVelocity.toFloat()
+    private val maxFlingSpeed = viewConfiguration.scaledMaximumFlingVelocity.toFloat()
+    private var latestEventTimestamp: Long = 0
+
+    private var flingVelocity: Float = 0f
+    private var flingTimestamp: Long = 0
+
+    @ExperimentalHorologistApi
+    override fun startFlingTracking(timestamp: Long) {
+        rotaryVelocityTracker.start(timestamp)
+        latestEventTimestamp = timestamp
+        previousVelocity = 0f
+    }
+
+    @ExperimentalHorologistApi
+    override fun observeEvent(timestamp: Long, delta: Float) {
+        rotaryVelocityTracker.move(timestamp, delta)
+        latestEventTimestamp = timestamp
+    }
+
+    @ExperimentalHorologistApi
+    override suspend fun trackFling(beforeFling: () -> Unit) {
+        val currentVelocity = rotaryVelocityTracker.velocity
+        debugLog { "currentVelocity: $currentVelocity" }
+
+        if (abs(currentVelocity) >= abs(previousVelocity)) {
+            flingTimestamp = latestEventTimestamp
+            flingVelocity = currentVelocity * flingScaleFactor
+        }
+        previousVelocity = currentVelocity
+
+        // Waiting for a fixed amount of time before checking the fling
+        delay(flingTimeframe)
+
+        // For making a fling 2 criteria should be met:
+        // 1) no more than
+        // `rangeToFling` ms should pass between last fling detection
+        // and the time of last motion event
+        // 2) flingVelocity should exceed the minFlingSpeed
+        debugLog {
+            "Check fling:  flingVelocity: $flingVelocity " +
+                    "minFlingSpeed: $minFlingSpeed, maxFlingSpeed: $maxFlingSpeed"
+        }
+        if (latestEventTimestamp - flingTimestamp < timeRangeToFling &&
+                abs(flingVelocity) > minFlingSpeed
+        ) {
+            // Stops scrollAnimationCoroutine because a fling will be performed
+            beforeFling()
+            val velocity = flingVelocity.coerceIn(-maxFlingSpeed, maxFlingSpeed)
+            scrollableState.scroll(MutatePriority.UserInput) {
+                with(flingBehavior) {
+                    debugLog { "Flinging with velocity $velocity" }
+                    performFling(velocity)
+                }
+            }
+        }
+    }
+}
+
+/**
+ * An interface for flinging with rotary
+ */
+@ExperimentalHorologistApi
+public interface RotaryFlingBehavior {
+
+    /**
+     * Observing new event within a fling tracking session with new timestamp and delta
+     */
+    @ExperimentalHorologistApi
+    public fun observeEvent(timestamp: Long, delta: Float)
+
+    /**
+     * Performing fling if necessary and calling [beforeFling] lambda before it is triggered
+     */
+    @ExperimentalHorologistApi
+    public suspend fun trackFling(beforeFling: () -> Unit)
+
+    /**
+     * Starts a new fling tracking session
+     * with specified timestamp
+     */
+    @ExperimentalHorologistApi
+    public fun startFlingTracking(timestamp: Long)
+}
+
+/**
+ * An interface for snapping with rotary
+ */
+@ExperimentalHorologistApi
+public interface RotarySnapBehavior {
+
+    /**
+     * Preparing snapping. This method should be called before [snapToTargetItem] is called.
+     *
+     * Snapping is done for current + [moveForElements] items.
+     *
+     * If [sequentialSnap] is true, items are summed up together.
+     * For example, if [prepareSnapForItems] is called with
+     * [moveForElements] = 2, 3, 5 -> then the snapping will happen to current + 10 items
+     *
+     * If [sequentialSnap] is false, then [moveForElements] are not summed up together.
+     */
+    public fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean)
+
+    /**
+     * Performs snapping to the closest item.
+     */
+    public suspend fun snapToClosestItem()
+
+    /**
+     * Returns true if top edge was reached
+     */
+    public fun topEdgeReached(): Boolean
+
+    /**
+     * Returns true if bottom edge was reached
+     */
+    public fun bottomEdgeReached(): Boolean
+
+    /**
+     * Performs snapping to the specified in [prepareSnapForItems] element
+     */
+    public suspend fun snapToTargetItem()
+}
+
+/**
+ * A rotary event object which contains a [timestamp] of the rotary event and a scrolled [delta].
+ */
+@ExperimentalHorologistApi
+public data class TimestampedDelta(val timestamp: Long, val delta: Float)
+
+/** Animation implementation of [RotaryScrollBehavior].
+ * This class does a smooth animation when the scroll by N pixels is done.
+ * This animation works well on Rsb(high-res) and Bezel(low-res) devices.
+ */
+@ExperimentalHorologistApi
+public class AnimationScrollBehavior(
+        private val scrollableState: ScrollableState,
+) : RotaryScrollBehavior {
+    private var sequentialAnimation = false
+    private var scrollAnimation = AnimationState(0f)
+    private var prevPosition = 0f
+
+    @ExperimentalHorologistApi
+    override suspend fun handleEvent(targetValue: Float) {
+        scrollableState.scroll(MutatePriority.UserInput) {
+            debugLog { "ScrollAnimation value before start: ${scrollAnimation.value}" }
+
+            scrollAnimation.animateTo(
+                    targetValue,
+                    animationSpec = spring(),
+                    sequentialAnimation = sequentialAnimation,
+            ) {
+                val delta = value - prevPosition
+                debugLog { "Animated by $delta, value: $value" }
+                scrollBy(delta)
+                prevPosition = value
+                sequentialAnimation = value != this.targetValue
+            }
+        }
+    }
+}
+
+/**
+ * An animated implementation of [RotarySnapBehavior]. Uses animateScrollToItem
+ * method for snapping to the Nth item
+ */
+@ExperimentalHorologistApi
+public class DefaultSnapBehavior(
+        private val rotaryScrollAdapter: RotaryScrollAdapter,
+        private val snapParameters: SnapParameters,
+) : RotarySnapBehavior {
+    private var snapTarget: Int = rotaryScrollAdapter.currentItemIndex()
+    private var sequentialSnap: Boolean = false
+
+    private var anim = AnimationState(0f)
+    private var expectedDistance = 0f
+
+    private val defaultStiffness = 200f
+    private var snapTargetUpdated = true
+
+    @ExperimentalHorologistApi
+    override fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean) {
+        this.sequentialSnap = sequentialSnap
+        if (sequentialSnap) {
+            snapTarget += moveForElements
+        } else {
+            snapTarget = rotaryScrollAdapter.currentItemIndex() + moveForElements
+        }
+        snapTargetUpdated = true
+        snapTarget = snapTarget.coerceIn(0 until rotaryScrollAdapter.totalItemsCount())
+    }
+
+    override suspend fun snapToClosestItem() {
+        // Snapping to the closest item by using performFling method with 0 speed
+        rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+            debugLog { "snap to closest item" }
+            var prevPosition = 0f
+            AnimationState(0f).animateTo(
+                    targetValue = -rotaryScrollAdapter.currentItemOffset(),
+                    animationSpec = tween(durationMillis = 100, easing = FastOutSlowInEasing),
+            ) {
+                val animDelta = value - prevPosition
+                scrollBy(animDelta)
+                prevPosition = value
+            }
+            snapTarget = rotaryScrollAdapter.currentItemIndex()
+        }
+    }
+
+    override fun topEdgeReached(): Boolean = snapTarget <= 0
+
+    override fun bottomEdgeReached(): Boolean =
+            snapTarget >= rotaryScrollAdapter.totalItemsCount() - 1
+
+    override suspend fun snapToTargetItem() {
+        if (sequentialSnap) {
+            anim = anim.copy(0f)
+        } else {
+            anim = AnimationState(0f)
+        }
+        rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+            // If snapTargetUpdated is true - then the target was updated so we
+            // need to do snap again
+            while (snapTargetUpdated) {
+                snapTargetUpdated = false
+                var latestCenterItem: Int
+                var continueFirstScroll = true
+                debugLog { "snapTarget $snapTarget" }
+                while (continueFirstScroll) {
+                    latestCenterItem = rotaryScrollAdapter.currentItemIndex()
+                    anim = anim.copy(0f)
+                    expectedDistance = expectedDistanceTo(snapTarget, snapParameters.snapOffset)
+                    debugLog {
+                        "expectedDistance = $expectedDistance, " +
+                                "scrollableState.centerItemScrollOffset " +
+                                "${rotaryScrollAdapter.currentItemOffset()}"
+                    }
+                    continueFirstScroll = false
+                    var prevPosition = 0f
+
+                    anim.animateTo(
+                            expectedDistance,
+                            animationSpec = SpringSpec(
+                                    stiffness = defaultStiffness,
+                                    visibilityThreshold = 0.1f,
+                            ),
+                            sequentialAnimation = (anim.velocity != 0f),
+                    ) {
+                        val animDelta = value - prevPosition
+                        debugLog {
+                            "First animation, value:$value, velocity:$velocity, " +
+                                    "animDelta:$animDelta"
+                        }
+
+                        // Exit animation if snap target was updated
+                        if (snapTargetUpdated) cancelAnimation()
+
+                        scrollBy(animDelta)
+                        prevPosition = value
+
+                        if (latestCenterItem != rotaryScrollAdapter.currentItemIndex()) {
+                            continueFirstScroll = true
+                            cancelAnimation()
+                            return@animateTo
+                        }
+
+                        debugLog { "centerItemIndex =  ${rotaryScrollAdapter.currentItemIndex()}" }
+                        if (rotaryScrollAdapter.currentItemIndex() == snapTarget) {
+                            debugLog { "Target is visible. Cancelling first animation" }
+                            debugLog {
+                                "scrollableState.centerItemScrollOffset " +
+                                        "${rotaryScrollAdapter.currentItemOffset()}"
+                            }
+                            expectedDistance = -rotaryScrollAdapter.currentItemOffset()
+                            continueFirstScroll = false
+                            cancelAnimation()
+                            return@animateTo
+                        }
+                    }
+                }
+                // Exit animation if snap target was updated
+                if (snapTargetUpdated) continue
+
+                anim = anim.copy(0f)
+                var prevPosition = 0f
+                anim.animateTo(
+                        expectedDistance,
+                        animationSpec = SpringSpec(
+                                stiffness = defaultStiffness,
+                                visibilityThreshold = 0.1f,
+                        ),
+                        sequentialAnimation = (anim.velocity != 0f),
+                ) {
+                    // Exit animation if snap target was updated
+                    if (snapTargetUpdated) cancelAnimation()
+
+                    val animDelta = value - prevPosition
+                    debugLog { "Final animation. velocity:$velocity, animDelta:$animDelta" }
+                    scrollBy(animDelta)
+                    prevPosition = value
+                }
+            }
+        }
+    }
+
+    private fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
+        val averageSize = rotaryScrollAdapter.averageItemSize()
+        val indexesDiff = index - rotaryScrollAdapter.currentItemIndex()
+        debugLog { "Average size $averageSize" }
+        return (averageSize * indexesDiff) +
+                targetScrollOffset - rotaryScrollAdapter.currentItemOffset()
+    }
+}
+
+/**
+ * A modifier which handles rotary events.
+ * It accepts ScrollHandler as the input - a class where main logic about how
+ * scroll should be handled is lying
+ */
+@ExperimentalHorologistApi
+@OptIn(ExperimentalComposeUiApi::class)
+public fun Modifier.rotaryHandler(
+        rotaryScrollHandler: RotaryScrollHandler,
+        // TODO: batching causes additional delays. Return once it's clear that
+        //  we will use it
+        /* batchTimeframe: Long = 0L,*/
+        reverseDirection: Boolean,
+        rotaryHaptics: RotaryHapticHandler,
+): Modifier = composed {
+    val channel = rememberTimestampChannel()
+    val eventsFlow = remember(channel) { channel.receiveAsFlow() }
+
+    composed {
+        LaunchedEffect(eventsFlow) {
+            eventsFlow
+                    // TODO: batching causes additional delays. Return once it's clear that
+                    //  we will use it
+                    // Do we really need to do this on this level?
+//                .batchRequestsWithinTimeframe(batchTimeframe)
+                    .collectLatest {
+                        debugLog {
+                            "Scroll event received: " +
+                                    "delta:${it.delta}, timestamp:${it.timestamp}"
+                        }
+                        rotaryScrollHandler.handleScrollEvent(this, it, rotaryHaptics)
+                    }
+        }
+        this
+                .onRotaryScrollEvent {
+                    // Okay to ignore the ChannelResult returned from trySend because it is conflated
+                    // (see rememberTimestampChannel()).
+                    @Suppress("UNUSED_VARIABLE")
+                    val unused = channel.trySend(
+                            TimestampedDelta(
+                                    it.uptimeMillis,
+                                    it.verticalScrollPixels * if (reverseDirection) -1f else 1f,
+                            ),
+                    )
+                    true
+                }
+    }
+}
+
+/**
+ * Batching requests for scrolling events. This function combines all events together
+ * (except first) within specified timeframe. Should help with performance on high-res devices.
+ */
+@ExperimentalHorologistApi
+@OptIn(ExperimentalCoroutinesApi::class)
+public fun Flow<TimestampedDelta>.batchRequestsWithinTimeframe(
+        timeframe: Long
+): Flow<TimestampedDelta> {
+    var delta = 0f
+    var lastTimestamp = -timeframe
+    return if (timeframe == 0L) {
+        this
+    } else {
+        this.transformLatest {
+            delta += it.delta
+            debugLog { "Batching requests. delta:$delta" }
+            if (lastTimestamp + timeframe <= it.timestamp) {
+                lastTimestamp = it.timestamp
+                debugLog { "No events before, delta= $delta" }
+                emit(TimestampedDelta(it.timestamp, delta))
+            } else {
+                delay(timeframe)
+                debugLog { "After delay, delta= $delta" }
+                if (delta > 0f) {
+                    emit(TimestampedDelta(it.timestamp, delta))
+                }
+            }
+            delta = 0f
+        }
+    }
+}
+
+/**
+ * A scroll handler for RSB(high-res) without snapping and with or without fling
+ * A list is scrolled by the number of pixels received from the rotary device.
+ *
+ * This class is a little bit different from LowResScrollHandler class - it has a filtering
+ * for events which are coming with wrong sign ( this happens to rsb devices,
+ * especially at the end of the scroll)
+ *
+ * This scroll handler supports fling. It can be set with [RotaryFlingBehavior].
+ */
+internal class HighResRotaryScrollHandler(
+        private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+        private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+        private val hapticsThreshold: Long = 50,
+) : RotaryScrollHandler {
+
+    // This constant is specific for high-res devices. Because that input values
+    // can sometimes come with different sign, we have to filter them in this threshold
+    private val gestureThresholdTime = 200L
+    private var scrollJob: Job = CompletableDeferred<Unit>()
+    private var flingJob: Job = CompletableDeferred<Unit>()
+
+    private var previousScrollEventTime = 0L
+    private var rotaryScrollDistance = 0f
+
+    private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+    private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+    override suspend fun handleScrollEvent(
+            coroutineScope: CoroutineScope,
+            event: TimestampedDelta,
+            rotaryHaptics: RotaryHapticHandler,
+    ) {
+        val time = event.timestamp
+        val isOppositeScrollValue = isOppositeValueAfterScroll(event.delta)
+
+        if (isNewScrollEvent(time)) {
+            debugLog { "New scroll event" }
+            resetTracking(time)
+            rotaryScrollDistance = event.delta
+        } else {
+            // Due to the physics of Rotary side button, some events might come
+            // with an opposite axis value - either at the start or at the end of the motion.
+            // We don't want to use these values for fling calculations.
+            if (!isOppositeScrollValue) {
+                rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+            } else {
+                debugLog { "Opposite value after scroll :${event.delta}" }
+            }
+            rotaryScrollDistance += event.delta
+        }
+
+        scrollJob.cancel()
+
+        rotaryHaptics.handleScrollHaptic(event.delta)
+        debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+        previousScrollEventTime = time
+        scrollJob = coroutineScope.async {
+            scrollBehavior.handleEvent(rotaryScrollDistance)
+        }
+
+        if (rotaryFlingBehavior != null) {
+            flingJob.cancel()
+            flingJob = coroutineScope.async {
+                rotaryFlingBehavior?.trackFling(beforeFling = {
+                    debugLog { "Calling before fling section" }
+                    scrollJob.cancel()
+                    scrollBehavior = scrollBehaviorFactory()
+                })
+            }
+        }
+    }
+
+    private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+            sign(rotaryScrollDistance) * sign(delta) == -1f &&
+                    (abs(delta) < abs(rotaryScrollDistance))
+
+    private fun isNewScrollEvent(timestamp: Long): Boolean {
+        val timeDelta = timestamp - previousScrollEventTime
+        return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+    }
+
+    private fun resetTracking(timestamp: Long) {
+        scrollBehavior = scrollBehaviorFactory()
+        rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+        rotaryFlingBehavior?.startFlingTracking(timestamp)
+    }
+}
+
+/**
+ * A scroll handler for Bezel(low-res) without snapping.
+ * This scroll handler supports fling. It can be set with RotaryFlingBehavior.
+ */
+internal class LowResRotaryScrollHandler(
+        private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+        private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+
+    private val gestureThresholdTime = 200L
+    private var previousScrollEventTime = 0L
+    private var rotaryScrollDistance = 0f
+
+    private var scrollJob: Job = CompletableDeferred<Unit>()
+    private var flingJob: Job = CompletableDeferred<Unit>()
+
+    private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+    private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+    override suspend fun handleScrollEvent(
+            coroutineScope: CoroutineScope,
+            event: TimestampedDelta,
+            rotaryHaptics: RotaryHapticHandler,
+    ) {
+        val time = event.timestamp
+
+        if (isNewScrollEvent(time)) {
+            resetTracking(time)
+            rotaryScrollDistance = event.delta
+        } else {
+            rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+            rotaryScrollDistance += event.delta
+        }
+
+        scrollJob.cancel()
+        flingJob.cancel()
+
+        rotaryHaptics.handleScrollHaptic(event.delta)
+        debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+        previousScrollEventTime = time
+        scrollJob = coroutineScope.async {
+            scrollBehavior.handleEvent(rotaryScrollDistance)
+        }
+
+        flingJob = coroutineScope.async {
+            rotaryFlingBehavior?.trackFling(
+                    beforeFling = {
+                        debugLog { "Calling before fling section" }
+                        scrollJob.cancel()
+                        scrollBehavior = scrollBehaviorFactory()
+                    },
+            )
+        }
+    }
+
+    private fun isNewScrollEvent(timestamp: Long): Boolean {
+        val timeDelta = timestamp - previousScrollEventTime
+        return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+    }
+
+    private fun resetTracking(timestamp: Long) {
+        scrollBehavior = scrollBehaviorFactory()
+        debugLog { "Velocity tracker reset" }
+        rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+        rotaryFlingBehavior?.startFlingTracking(timestamp)
+    }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling
+ * Snapping happens after a threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class HighResSnapHandler(
+        private val resistanceFactor: Float,
+        private val thresholdBehaviorFactory: () -> ThresholdBehavior,
+        private val snapBehaviourFactory: () -> RotarySnapBehavior,
+        private val scrollBehaviourFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+    private val gestureThresholdTime = 200L
+    private val snapDelay = 100L
+    private val maxSnapsPerEvent = 2
+
+    private var scrollJob: Job = CompletableDeferred<Unit>()
+    private var snapJob: Job = CompletableDeferred<Unit>()
+
+    private var previousScrollEventTime = 0L
+    private var snapAccumulator = 0f
+    private var rotaryScrollDistance = 0f
+    private var scrollInProgress = false
+
+    private var snapBehaviour = snapBehaviourFactory()
+    private var scrollBehaviour = scrollBehaviourFactory()
+    private var thresholdBehavior = thresholdBehaviorFactory()
+
+    private val scrollEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.5f, 1.0f)
+
+    override suspend fun handleScrollEvent(
+            coroutineScope: CoroutineScope,
+            event: TimestampedDelta,
+            rotaryHaptics: RotaryHapticHandler,
+    ) {
+        val time = event.timestamp
+
+        if (isNewScrollEvent(time)) {
+            debugLog { "New scroll event" }
+            resetTracking()
+            snapJob.cancel()
+            snapBehaviour = snapBehaviourFactory()
+            scrollBehaviour = scrollBehaviourFactory()
+            thresholdBehavior = thresholdBehaviorFactory()
+            thresholdBehavior.startThresholdTracking(time)
+            snapAccumulator = 0f
+            rotaryScrollDistance = 0f
+        }
+
+        if (!isOppositeValueAfterScroll(event.delta)) {
+            thresholdBehavior.observeEvent(event.timestamp, event.delta)
+        } else {
+            debugLog { "Opposite value after scroll :${event.delta}" }
+        }
+
+        thresholdBehavior.applySmoothing()
+        val snapThreshold = thresholdBehavior.snapThreshold()
+
+        snapAccumulator += event.delta
+        if (!snapJob.isActive) {
+            val resistanceCoeff =
+                    1 - scrollEasing.transform(rotaryScrollDistance.absoluteValue / snapThreshold)
+            rotaryScrollDistance += event.delta * resistanceCoeff
+        }
+
+        debugLog { "Snap accumulator: $snapAccumulator" }
+        debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+        debugLog { "snapThreshold: $snapThreshold" }
+        previousScrollEventTime = time
+
+        if (abs(snapAccumulator) > snapThreshold) {
+            scrollInProgress = false
+            scrollBehaviour = scrollBehaviourFactory()
+            scrollJob.cancel()
+
+            val snapDistance = (snapAccumulator / snapThreshold).toInt()
+                    .coerceIn(-maxSnapsPerEvent..maxSnapsPerEvent)
+            snapAccumulator -= snapThreshold * snapDistance
+            val sequentialSnap = snapJob.isActive
+
+            debugLog {
+                "Snap threshold reached: snapDistance:$snapDistance, " +
+                        "sequentialSnap: $sequentialSnap, " +
+                        "snap accumulator remaining: $snapAccumulator"
+            }
+            if ((!snapBehaviour.topEdgeReached() && snapDistance < 0) ||
+                    (!snapBehaviour.bottomEdgeReached() && snapDistance > 0)
+            ) {
+                rotaryHaptics.handleSnapHaptic(event.delta)
+            }
+
+            snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+            if (!snapJob.isActive) {
+                snapJob.cancel()
+                snapJob = coroutineScope.async {
+                    debugLog { "Snap started" }
+                    try {
+                        snapBehaviour.snapToTargetItem()
+                    } finally {
+                        debugLog { "Snap called finally" }
+                    }
+                }
+            }
+            rotaryScrollDistance = 0f
+        } else {
+            if (!snapJob.isActive) {
+                scrollJob.cancel()
+                debugLog { "Scrolling for $rotaryScrollDistance/$resistanceFactor px" }
+                scrollJob = coroutineScope.async {
+                    scrollBehaviour.handleEvent(rotaryScrollDistance / resistanceFactor)
+                }
+                delay(snapDelay)
+                scrollInProgress = false
+                scrollBehaviour = scrollBehaviourFactory()
+                rotaryScrollDistance = 0f
+                snapAccumulator = 0f
+                snapBehaviour.prepareSnapForItems(0, false)
+
+                snapJob.cancel()
+                snapJob = coroutineScope.async {
+                    snapBehaviour.snapToClosestItem()
+                }
+            }
+        }
+    }
+
+    private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+            sign(rotaryScrollDistance) * sign(delta) == -1f &&
+                    (abs(delta) < abs(rotaryScrollDistance))
+
+    private fun isNewScrollEvent(timestamp: Long): Boolean {
+        val timeDelta = timestamp - previousScrollEventTime
+        return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+    }
+
+    private fun resetTracking() {
+        scrollInProgress = true
+    }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling
+ * Snapping happens after a threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class LowResSnapHandler(
+        private val snapBehaviourFactory: () -> RotarySnapBehavior,
+) : RotaryScrollHandler {
+    private val gestureThresholdTime = 200L
+
+    private var snapJob: Job = CompletableDeferred<Unit>()
+
+    private var previousScrollEventTime = 0L
+    private var snapAccumulator = 0f
+    private var scrollInProgress = false
+
+    private var snapBehaviour = snapBehaviourFactory()
+
+    override suspend fun handleScrollEvent(
+            coroutineScope: CoroutineScope,
+            event: TimestampedDelta,
+            rotaryHaptics: RotaryHapticHandler,
+    ) {
+        val time = event.timestamp
+
+        if (isNewScrollEvent(time)) {
+            debugLog { "New scroll event" }
+            resetTracking()
+            snapJob.cancel()
+            snapBehaviour = snapBehaviourFactory()
+            snapAccumulator = 0f
+        }
+
+        snapAccumulator += event.delta
+
+        debugLog { "Snap accumulator: $snapAccumulator" }
+
+        previousScrollEventTime = time
+
+        if (abs(snapAccumulator) > 1f) {
+            scrollInProgress = false
+
+            val snapDistance = sign(snapAccumulator).toInt()
+            rotaryHaptics.handleSnapHaptic(event.delta)
+            val sequentialSnap = snapJob.isActive
+            debugLog {
+                "Snap threshold reached: snapDistance:$snapDistance, " +
+                        "sequentialSnap: $sequentialSnap, " +
+                        "snap accumulator: $snapAccumulator"
+            }
+
+            snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+            if (!snapJob.isActive) {
+                snapJob.cancel()
+                snapJob = coroutineScope.async {
+                    debugLog { "Snap started" }
+                    try {
+                        snapBehaviour.snapToTargetItem()
+                    } finally {
+                        debugLog { "Snap called finally" }
+                    }
+                }
+            }
+            snapAccumulator = 0f
+        }
+    }
+
+    private fun isNewScrollEvent(timestamp: Long): Boolean {
+        val timeDelta = timestamp - previousScrollEventTime
+        return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+    }
+
+    private fun resetTracking() {
+        scrollInProgress = true
+    }
+}
+
+internal class ThresholdBehavior(
+        private val rotaryScrollAdapter: RotaryScrollAdapter,
+        private val thresholdDivider: Float,
+        private val minVelocity: Float = 300f,
+        private val maxVelocity: Float = 3000f,
+        private val smoothingConstant: Float = 0.4f,
+) {
+    private val thresholdDividerEasing: Easing = CubicBezierEasing(0.5f, 0.0f, 0.5f, 1.0f)
+
+    private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+    private var smoothedVelocity = 0f
+    fun startThresholdTracking(time: Long) {
+        rotaryVelocityTracker.start(time)
+        smoothedVelocity = 0f
+    }
+
+    fun observeEvent(timestamp: Long, delta: Float) {
+        rotaryVelocityTracker.move(timestamp, delta)
+    }
+
+    fun applySmoothing() {
+        if (rotaryVelocityTracker.velocity != 0.0f) {
+            // smooth the velocity
+            smoothedVelocity = exponentialSmoothing(
+                    currentVelocity = rotaryVelocityTracker.velocity.absoluteValue,
+                    prevVelocity = smoothedVelocity,
+                    smoothingConstant = smoothingConstant,
+            )
+        }
+        debugLog { "rotaryVelocityTracker velocity: ${rotaryVelocityTracker.velocity}" }
+        debugLog { "SmoothedVelocity: $smoothedVelocity" }
+    }
+
+    fun snapThreshold(): Float {
+        val thresholdDividerFraction =
+                thresholdDividerEasing.transform(
+                        inverseLerp(
+                                minVelocity,
+                                maxVelocity,
+                                smoothedVelocity,
+                        ),
+                )
+        return rotaryScrollAdapter.averageItemSize() / lerp(
+                1f,
+                thresholdDivider,
+                thresholdDividerFraction,
+        )
+    }
+
+    private fun exponentialSmoothing(
+            currentVelocity: Float,
+            prevVelocity: Float,
+            smoothingConstant: Float,
+    ): Float =
+            smoothingConstant * currentVelocity + (1 - smoothingConstant) * prevVelocity
+}
+
+@Composable
+private fun rememberTimestampChannel() = remember {
+    Channel<TimestampedDelta>(capacity = Channel.CONFLATED)
+}
+
+private fun inverseLerp(start: Float, stop: Float, value: Float): Float {
+    return ((value - start) / (stop - start)).coerceIn(0f, 1f)
+}
diff --git a/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt
new file mode 100644
index 0000000..6e627c6
--- /dev/null
+++ b/packages/CredentialManager/horologist/src/com/google/android/horologist/compose/rotaryinput/RotaryVelocityTracker.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 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
+ *
+ *      https://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.google.android.horologist.compose.rotaryinput
+
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+
+/**
+ * A wrapper around VelocityTracker1D to provide support for rotary input.
+ */
+public class RotaryVelocityTracker {
+    private var velocityTracker: VelocityTracker1D = VelocityTracker1D(true)
+
+    /**
+     * Retrieve the last computed velocity.
+     */
+    public val velocity: Float
+        get() = velocityTracker.calculateVelocity()
+
+    /**
+     * Start tracking motion.
+     */
+    public fun start(currentTime: Long) {
+        velocityTracker.resetTracking()
+        velocityTracker.addDataPoint(currentTime, 0f)
+    }
+
+    /**
+     * Continue tracking motion as the input rotates.
+     */
+    public fun move(currentTime: Long, delta: Float) {
+        velocityTracker.addDataPoint(currentTime, delta)
+    }
+
+    /**
+     * Stop tracking motion.
+     */
+    public fun end() {
+        velocityTracker.resetTracking()
+    }
+}
diff --git a/packages/CredentialManager/res/xml/autofill_service_configuration.xml b/packages/CredentialManager/res/xml/autofill_service_configuration.xml
new file mode 100644
index 0000000..25cc094
--- /dev/null
+++ b/packages/CredentialManager/res/xml/autofill_service_configuration.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+   Sample backup rules file; uncomment and customize as necessary.
+   See https://developer.android.com/guide/topics/data/autobackup
+   for details.
+   Note: This file is ignored for devices older that API 31
+   See https://developer.android.com/about/versions/12/backup-restore
+-->
+<autofill-service-configuration
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:supportsInlineSuggestions="true"/>
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/README.md b/packages/CredentialManager/shared/README.md
new file mode 100644
index 0000000..d2375a0
--- /dev/null
+++ b/packages/CredentialManager/shared/README.md
@@ -0,0 +1,2 @@
+This folder is to place the common code that will be shared between the phone project and the wear
+project of the Credential Manager implementation.
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 8361877..473d7b6f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -19,6 +19,7 @@
 import android.app.slice.Slice
 import android.content.ComponentName
 import android.content.Context
+import android.content.pm.PackageInfo
 import android.content.pm.PackageManager
 import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
 import android.credentials.ui.AuthenticationEntry
@@ -67,7 +68,7 @@
     appPackageName: String
 ): String? {
     return try {
-        val pkgInfo = pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+        val pkgInfo = getPackageInfo(pm, appPackageName)
         val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
         applicationInfo.loadSafeLabel(
             pm, 0f,
@@ -90,10 +91,7 @@
         // Test data has only package name not component name.
         // For test data usage only.
         try {
-            val pkgInfo = pm.getPackageInfo(
-                providerFlattenedComponentName,
-                PackageManager.PackageInfoFlags.of(0)
-            )
+            val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
             val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
             providerLabel =
                 applicationInfo.loadSafeLabel(
@@ -117,10 +115,7 @@
             // Added for mdoc use case where the provider may not need to register a service and
             // instead only relies on the registration api.
             try {
-                val pkgInfo = pm.getPackageInfo(
-                    component.packageName,
-                    PackageManager.PackageInfoFlags.of(0)
-                )
+                val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
                 val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
                 providerLabel =
                     applicationInfo.loadSafeLabel(
@@ -144,6 +139,19 @@
     }
 }
 
+private fun getPackageInfo(
+    pm: PackageManager,
+    packageName: String
+): PackageInfo {
+    val flags = PackageManager.MATCH_INSTANT
+
+    return pm.getPackageInfo(
+       packageName,
+       PackageManager.PackageInfoFlags.of(
+               (flags).toLong())
+    )
+}
+
 /** Utility functions for converting CredentialManager data structures to or from UI formats. */
 class GetFlowUtils {
     companion object {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
new file mode 100644
index 0000000..943c2b4
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.credentialmanager.autofill
+
+import android.os.CancellationSignal
+import android.service.autofill.AutofillService
+import android.service.autofill.FillCallback
+import android.service.autofill.FillRequest
+import android.service.autofill.SaveRequest
+import android.service.autofill.SaveCallback
+
+class CredentialAutofillService : AutofillService() {
+    override fun onFillRequest(
+            request: FillRequest,
+            cancellationSignal: CancellationSignal,
+            callback: FillCallback
+    ) {
+        TODO("Not yet implemented")
+    }
+
+    override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
+        TODO("Not yet implemented")
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
new file mode 100644
index 0000000..639e8d1
--- /dev/null
+++ b/packages/CredentialManager/wear/Android.bp
@@ -0,0 +1,53 @@
+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"],
+}
+
+android_app {
+    name: "ClockworkCredentialManager",
+    defaults: ["platform_app_defaults"],
+    certificate: "platform",
+    manifest: "AndroidManifest.xml",
+    srcs: ["src/**/*.kt"],
+    resource_dirs: ["res"],
+
+    dex_preopt: {
+        profile_guided: true,
+        profile: "profile.txt.prof",
+    },
+
+    static_libs: [
+        "Horologist",
+        "PlatformComposeCore",
+        "androidx.activity_activity-compose",
+        "androidx.appcompat_appcompat",
+        "androidx.compose.foundation_foundation",
+        "androidx.compose.foundation_foundation-layout",
+        "androidx.compose.material_material-icons-core",
+        "androidx.compose.material_material-icons-extended",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.ui_ui",
+        "androidx.core_core-ktx",
+        "androidx.lifecycle_lifecycle-extensions",
+        "androidx.lifecycle_lifecycle-livedata",
+        "androidx.lifecycle_lifecycle-runtime-ktx",
+        "androidx.lifecycle_lifecycle-viewmodel-compose",
+        "androidx.wear.compose_compose-foundation",
+        "androidx.wear.compose_compose-material",
+        "androidx.wear.compose_compose-navigation",
+        "kotlinx-coroutines-core",
+    ],
+
+    platform_apis: true,
+    privileged: true,
+
+    kotlincflags: ["-Xjvm-default=all"],
+
+    optimize: {
+        proguard_compatibility: false,
+    },
+}
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 001a56d..90248734 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
- * Copyright (c) 2017 Google Inc.
+ * Copyright (c) 2023 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.credentialmanager">
 
+    <uses-feature android:name="android.hardware.type.watch" />
+
     <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
@@ -28,17 +30,15 @@
       android:dataExtractionRules="@xml/data_extraction_rules"
       android:fullBackupContent="@xml/backup_rules"
       android:label="@string/app_name"
-      android:supportsRtl="true"
-      android:theme="@style/Theme.CredentialSelector">
+      android:supportsRtl="true">
 
         <activity
-            android:name=".CredentialSelectorActivity"
+            android:name=".ui.CredentialSelectorActivity"
             android:exported="true"
             android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
             android:launchMode="singleTop"
             android:label="@string/app_name"
-            android:excludeFromRecents="true"
-            android:theme="@style/Theme.CredentialSelector">
+            android:excludeFromRecents="true">
         </activity>
   </application>
 
diff --git a/packages/CredentialManager/wear/README.md b/packages/CredentialManager/wear/README.md
new file mode 100644
index 0000000..b9d27b9
--- /dev/null
+++ b/packages/CredentialManager/wear/README.md
@@ -0,0 +1 @@
+This project is the wear implementation of the Credential Manager feature.
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
similarity index 68%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
index f7b2499..77fffaa 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,19 +14,29 @@
  * limitations under the License.
  */
 
+package com.android.credentialmanager.ui
+
 import android.os.Bundle
-import androidx.activity.compose.setContent
 import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.navigation.NavHostController
 import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.Text
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
 
 class CredentialSelectorActivity : ComponentActivity() {
+
+    lateinit var navController: NavHostController
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        setTheme(android.R.style.Theme_DeviceDefault)
+
         setContent {
+            navController = rememberSwipeDismissableNavController()
+
             MaterialTheme {
-                Text("Credential Manager entry point")
+                WearApp(navController = navController)
             }
         }
     }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
new file mode 100644
index 0000000..ee6ea5e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui
+
+sealed class Screen(
+    val route: String,
+) {
+    object Main : Screen("main")
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
new file mode 100644
index 0000000..5ec0c8c
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.0N
+ *
+ * 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(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui
+
+import androidx.compose.runtime.Composable
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
+import com.android.credentialmanager.ui.screens.MainScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.navscaffold.WearNavScaffold
+import com.google.android.horologist.compose.navscaffold.composable
+
+@Composable
+fun WearApp(
+    navController: NavHostController
+) {
+    val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
+    val navHostState =
+        rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
+
+    WearNavScaffold(
+        startDestination = Screen.Main.route,
+        navController = navController,
+        state = navHostState,
+    ) {
+        composable(Screen.Main.route) {
+            MainScreen()
+        }
+    }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
new file mode 100644
index 0000000..662d710
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui.screens
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material.Text
+
+@Composable
+fun MainScreen(modifier: Modifier = Modifier) {
+    Box(modifier = modifier, contentAlignment = Alignment.Center) {
+        Text("This is a placeholder for the main screen.")
+    }
+}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 5bd422b..a16f9f5 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -70,15 +70,18 @@
             </intent-filter>
         </activity>
 
+        <!-- NOTE: the workaround to fix the screen flash problem. Remember to check the problem
+            is resolved for new implementation -->
         <activity android:name=".InstallStaging"
-                android:exported="false" />
+                  android:theme="@style/Theme.AlertDialogActivity.NoDim"
+                  android:exported="false" />
 
         <activity android:name=".DeleteStagedFileOnResult"
             android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
             android:exported="false" />
 
         <activity android:name=".PackageInstallerActivity"
-                android:exported="false" />
+                  android:exported="false" />
 
         <activity android:name=".InstallInstalling"
                 android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index 9a06229..aa1fa16 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -32,4 +32,9 @@
         <item name="android:windowNoTitle">true</item>
     </style>
 
+    <style name="Theme.AlertDialogActivity.NoDim">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:backgroundDimAmount">0</item>
+    </style>
+
 </resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 80e8761..d97fb54 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -306,6 +306,7 @@
     }
 
     private void initiateInstall() {
+        bindUi();
         String pkgName = mPkgInfo.packageName;
         // Check if there is already a package on the device with this name
         // but it has been renamed to something else.
@@ -445,7 +446,6 @@
         if (mAppSnippet != null) {
             // load placeholder layout with OK button disabled until we override this layout in
             // startInstallConfirm
-            bindUi();
             checkIfAllowedAndInitiateInstall();
         }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 471f3b9..01596d2 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -32,6 +32,7 @@
 import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
 import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsOutlinedTextFieldPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsTextFieldPasswordPageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
 import com.android.settingslib.spa.gallery.page.FooterPageProvider
 import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
@@ -44,6 +45,7 @@
 import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
 import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
 import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
 import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
@@ -92,6 +94,8 @@
                 SettingsOutlinedTextFieldPageProvider,
                 SettingsExposedDropdownMenuBoxPageProvider,
                 SettingsExposedDropdownMenuCheckBoxProvider,
+                SettingsTextFieldPasswordPageProvider,
+                SearchScaffoldPageProvider,
             ),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
index b74af21..4875ea9 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -39,6 +39,8 @@
                 .build(),
             SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
                 .build(),
+            SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
+                .build(),
         )
     }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
index 6d22e6a..5ffbe8ba 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
@@ -19,7 +19,7 @@
 import android.os.Bundle
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.tooling.preview.Preview
@@ -45,13 +45,13 @@
 
     @Composable
     override fun Page(arguments: Bundle?) {
-        var selectedItem by remember { mutableStateOf("item1") }
+        var selectedItem by remember { mutableIntStateOf(-1) }
         val options = listOf("item1", "item2", "item3")
         RegularScaffold(title = TITLE) {
             SettingsExposedDropdownMenuBox(
                 label = exposedDropdownMenuBoxLabel,
                 options = options,
-                selectedOptionText = selectedItem,
+                selectedOptionIndex = selectedItem,
                 enabled = true,
                 onselectedOptionTextChange = { selectedItem = it })
         }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
index 292e002..37c8eef 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
@@ -46,21 +46,23 @@
     @Composable
     override fun Page(arguments: Bundle?) {
         RegularScaffold(title = TITLE) {
-            SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+            SettingsExposedDropdownMenuCheckBox(
+                label = exposedDropdownMenuCheckBoxLabel,
                 options = options,
                 selectedOptionsState = remember { selectedOptionsState1 },
                 enabled = true,
-                onselectedOptionStateChange = {})
+                onSelectedOptionStateChange = {},
+            )
         }
     }
 
     fun buildInjectEntry(): SettingsEntryBuilder {
         return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
-                Preference(object : PreferenceModel {
-                    override val title = TITLE
-                    override val onClick = navigator(name)
-                })
-            }
+            Preference(object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            })
+        }
     }
 }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt
new file mode 100644
index 0000000..fc51466e
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsTextFieldPasswordPageProvider.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.settingslib.spa.gallery.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsTextFieldPassword"
+
+object SettingsTextFieldPasswordPageProvider : SettingsPageProvider {
+    override val name = "SettingsTextFieldPassword"
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        var value by remember { mutableStateOf("value") }
+        RegularScaffold(title = TITLE) {
+            SettingsTextFieldPassword(
+                value = value,
+                label = "label",
+                onTextChange = { value = it })
+        }
+    }
+
+    fun buildInjectEntry(): SettingsEntryBuilder {
+        return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+            .setUiLayoutFn {
+                Preference(object : PreferenceModel {
+                    override val title = TITLE
+                    override val onClick = navigator(name)
+                })
+            }
+    }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsTextFieldPasswordPagePreview() {
+    SettingsTheme {
+        SettingsTextFieldPasswordPageProvider.Page(null)
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6cac220..b339b44 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -41,6 +41,7 @@
 import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
 import com.android.settingslib.spa.gallery.page.SliderPageProvider
 import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
 import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
 import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
 import com.android.settingslib.spa.widget.scaffold.HomeScaffold
@@ -55,6 +56,7 @@
             PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
+            SearchScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt
new file mode 100644
index 0000000..a1ab35b
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SearchScaffoldPageProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.spa.gallery.scaffold
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.SearchScaffold
+import com.android.settingslib.spa.widget.ui.PlaceholderTitle
+
+private const val TITLE = "Sample SearchScaffold"
+
+object SearchScaffoldPageProvider : SettingsPageProvider {
+    override val name = "SearchScaffold"
+
+    private val owner = createSettingsPage()
+
+    fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
+        .setUiLayoutFn {
+            Preference(object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            })
+        }
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        Page()
+    }
+}
+
+@Composable
+private fun Page() {
+    SearchScaffold(title = TITLE) { bottomPadding, searchQuery ->
+        PlaceholderTitle("Search query: ${searchQuery.value}")
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 08e3a27..9f8c868 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,6 +22,7 @@
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -126,18 +127,22 @@
     allProvider: Collection<SettingsPageProvider>,
     content: @Composable (SettingsPage) -> Unit,
 ) {
-    NavHost(
-        navController = navController,
-        startDestination = NullPageProvider.name,
-    ) {
-        composable(NullPageProvider.name) {}
-        for (spp in allProvider) {
-            animatedComposable(
-                route = spp.name + spp.parameter.navRoute(),
-                arguments = spp.parameter,
-            ) { navBackStackEntry ->
-                val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
-                content(page)
+    // TODO(b/298520326): Remove Box after the issue is fixed.
+    // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03.
+    Box {
+        NavHost(
+            navController = navController,
+            startDestination = NullPageProvider.name,
+        ) {
+            composable(NullPageProvider.name) {}
+            for (spp in allProvider) {
+                animatedComposable(
+                    route = spp.name + spp.parameter.navRoute(),
+                    arguments = spp.parameter,
+                ) { navBackStackEntry ->
+                    val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
+                    content(page)
+                }
             }
         }
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
index ec43aab..0d6c064 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
@@ -41,9 +41,9 @@
 fun SettingsExposedDropdownMenuBox(
     label: String,
     options: List<String>,
-    selectedOptionText: String,
+    selectedOptionIndex: Int,
     enabled: Boolean,
-    onselectedOptionTextChange: (String) -> Unit,
+    onselectedOptionTextChange: (Int) -> Unit,
 ) {
     var expanded by remember { mutableStateOf(false) }
     ExposedDropdownMenuBox(
@@ -58,8 +58,8 @@
             modifier = Modifier
                 .menuAnchor()
                 .fillMaxWidth(),
-            value = selectedOptionText,
-            onValueChange = onselectedOptionTextChange,
+            value = options.getOrElse(selectedOptionIndex) { "" },
+            onValueChange = { },
             label = { Text(text = label) },
             trailingIcon = {
                 ExposedDropdownMenuDefaults.TrailingIcon(
@@ -81,7 +81,7 @@
                     DropdownMenuItem(
                         text = { Text(option) },
                         onClick = {
-                            onselectedOptionTextChange(option)
+                            onselectedOptionTextChange(options.indexOf(option))
                             expanded = false
                         },
                         contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
@@ -103,7 +103,7 @@
         SettingsExposedDropdownMenuBox(
             label = "ExposedDropdownMenuBoxLabel",
             options = options,
-            selectedOptionText = item1,
+            selectedOptionIndex = 0,
             enabled = true,
             onselectedOptionTextChange = {})
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
index 459a783..a258185 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
@@ -31,6 +31,7 @@
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -52,9 +53,9 @@
     options: List<String>,
     selectedOptionsState: SnapshotStateList<String>,
     enabled: Boolean,
-    onselectedOptionStateChange: (String) -> Unit,
+    onSelectedOptionStateChange: () -> Unit,
 ) {
-    var dropDownWidth by remember { mutableStateOf(0) }
+    var dropDownWidth by remember { mutableIntStateOf(0) }
     var expanded by remember { mutableStateOf(false) }
     ExposedDropdownMenuBox(
         expanded = expanded,
@@ -70,7 +71,7 @@
                 .menuAnchor()
                 .fillMaxWidth(),
             value = selectedOptionsState.joinToString(", "),
-            onValueChange = onselectedOptionStateChange,
+            onValueChange = {},
             label = { Text(text = label) },
             trailingIcon = {
                 ExposedDropdownMenuDefaults.TrailingIcon(
@@ -89,19 +90,22 @@
                 onDismissRequest = { expanded = false },
             ) {
                 options.forEach { option ->
-                    TextButton(modifier = Modifier
-                        .fillMaxHeight()
-                        .fillMaxWidth(), onClick = {
-                        if (selectedOptionsState.contains(option)) {
-                            selectedOptionsState.remove(
-                                option
-                            )
-                        } else {
-                            selectedOptionsState.add(
-                                option
-                            )
-                        }
-                    }) {
+                    TextButton(
+                        modifier = Modifier
+                            .fillMaxHeight()
+                            .fillMaxWidth(),
+                        onClick = {
+                            if (selectedOptionsState.contains(option)) {
+                                selectedOptionsState.remove(
+                                    option
+                                )
+                            } else {
+                                selectedOptionsState.add(
+                                    option
+                                )
+                            }
+                            onSelectedOptionStateChange()
+                        }) {
                         Row(
                             modifier = Modifier
                                 .fillMaxHeight()
@@ -109,9 +113,10 @@
                             horizontalArrangement = Arrangement.Start,
                             verticalAlignment = Alignment.CenterVertically
                         ) {
-                            Checkbox(checked = selectedOptionsState.contains(
-                                option
-                            ), onCheckedChange = {})
+                            Checkbox(
+                                checked = selectedOptionsState.contains(option),
+                                onCheckedChange = null,
+                            )
                             Text(text = option)
                         }
                     }
@@ -127,10 +132,11 @@
     val options = listOf("item1", "item2", "item3")
     val selectedOptionsState = remember { mutableStateListOf("item1", "item2") }
     SettingsTheme {
-        SettingsExposedDropdownMenuCheckBox(label = "label",
+        SettingsExposedDropdownMenuCheckBox(
+            label = "label",
             options = options,
             selectedOptionsState = selectedOptionsState,
             enabled = true,
-            onselectedOptionStateChange = {})
+            onSelectedOptionStateChange = {})
     }
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
new file mode 100644
index 0000000..d0a6188
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Visibility
+import androidx.compose.material.icons.outlined.VisibilityOff
+import androidx.compose.material3.Icon
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun SettingsTextFieldPassword(
+    value: String,
+    label: String,
+    onTextChange: (String) -> Unit,
+) {
+    var visibility by remember { mutableStateOf(false) }
+    OutlinedTextField(
+        modifier = Modifier
+            .padding(SettingsDimension.itemPadding)
+            .fillMaxWidth(),
+        value = value,
+        onValueChange = onTextChange,
+        label = { Text(text = label) },
+        keyboardOptions = KeyboardOptions(
+            keyboardType = KeyboardType.Password,
+            imeAction = ImeAction.Send
+        ),
+        trailingIcon = {
+            Icon(
+                imageVector = if (visibility) Icons.Outlined.VisibilityOff
+                else Icons.Outlined.Visibility,
+                contentDescription = "Visibility Icon",
+                modifier = Modifier
+                    .testTag("Visibility Icon")
+                    .size(SettingsDimension.itemIconSize)
+                    .toggleable(visibility) {
+                        visibility = !visibility
+                    },
+            )
+        },
+        visualTransformation = if (visibility) VisualTransformation.None
+        else PasswordVisualTransformation()
+    )
+}
+
+@Preview
+@Composable
+private fun SettingsTextFieldPasswordPreview() {
+    var value by remember { mutableStateOf("value") }
+    SettingsTheme {
+        SettingsTextFieldPassword(
+            value = value,
+            label = "label",
+            onTextChange = { value = it },
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index b77368a..3a0e51b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -32,8 +32,8 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.WindowInsetsSides
+import androidx.compose.foundation.layout.only
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.windowInsetsPadding
 import androidx.compose.material3.ExperimentalMaterial3Api
@@ -49,10 +49,8 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clipToBounds
@@ -73,7 +71,6 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
-import com.android.settingslib.spa.framework.compose.horizontalValues
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import kotlin.math.abs
@@ -129,16 +126,10 @@
 private fun Title(title: String, maxLines: Int = Int.MAX_VALUE) {
     Text(
         text = title,
-        modifier = Modifier
-            .padding(
-                WindowInsets.navigationBars
-                    .asPaddingValues()
-                    .horizontalValues()
-            )
-            .padding(
-                start = SettingsDimension.itemPaddingAround,
-                end = SettingsDimension.itemPaddingEnd,
-            ),
+        modifier = Modifier.padding(
+            start = SettingsDimension.itemPaddingAround,
+            end = SettingsDimension.itemPaddingEnd,
+        ),
         overflow = TextOverflow.Ellipsis,
         maxLines = maxLines,
     )
@@ -157,6 +148,15 @@
  * Represents the colors used by a top app bar in different states.
  * This implementation animates the container color according to the top app bar scroll state. It
  * does not animate the leading, headline, or trailing colors.
+ *
+ * @constructor create an instance with arbitrary colors, see [TopAppBarColors] for a
+ * factory method using the default material3 spec
+ * @param containerColor the color used for the background of this BottomAppBar. Use
+ * [Color.Transparent] to have no color.
+ * @param scrolledContainerColor the container color when content is scrolled behind it
+ * @param navigationIconContentColor the content color used for the navigation icon
+ * @param titleContentColor the content color used for the title
+ * @param actionIconContentColor the content color used for actions
  */
 @Stable
 private class TopAppBarColors(
@@ -248,7 +248,6 @@
             titleTextStyle = titleTextStyle,
             titleAlpha = 1f,
             titleVerticalArrangement = Arrangement.Center,
-            titleHorizontalArrangement = Arrangement.Start,
             titleBottomPadding = 0,
             hideTitleSemantics = false,
             navigationIcon = navigationIcon,
@@ -312,7 +311,7 @@
     // This will potentially animate or interpolate a transition between the container color and the
     // container's scrolled color according to the app bar's scroll state.
     val colorTransitionFraction = scrollBehavior?.state?.collapsedFraction ?: 0f
-    val appBarContainerColor by rememberUpdatedState(colors.containerColor(colorTransitionFraction))
+    val appBarContainerColor = colors.containerColor(colorTransitionFraction)
 
     // Wrap the given actions in a Row.
     val actionsRow = @Composable {
@@ -364,14 +363,17 @@
                 titleTextStyle = smallTitleTextStyle,
                 titleAlpha = topTitleAlpha,
                 titleVerticalArrangement = Arrangement.Center,
-                titleHorizontalArrangement = Arrangement.Start,
                 titleBottomPadding = 0,
                 hideTitleSemantics = hideTopRowSemantics,
                 navigationIcon = navigationIcon,
                 actions = actionsRow,
             )
             TopAppBarLayout(
-                modifier = Modifier.clipToBounds(),
+                modifier = Modifier
+                    // only apply the horizontal sides of the window insets padding, since the top
+                    // padding will always be applied by the layout above
+                    .windowInsetsPadding(windowInsets.only(WindowInsetsSides.Horizontal))
+                    .clipToBounds(),
                 heightPx = maxHeightPx.floatValue - pinnedHeightPx +
                     (scrollBehavior?.state?.heightOffset ?: 0f),
                 navigationIconContentColor = colors.navigationIconContentColor,
@@ -392,7 +394,6 @@
                 titleTextStyle = titleTextStyle,
                 titleAlpha = bottomTitleAlpha,
                 titleVerticalArrangement = Arrangement.Bottom,
-                titleHorizontalArrangement = Arrangement.Start,
                 titleBottomPadding = titleBottomPaddingPx,
                 hideTitleSemantics = hideBottomRowSemantics,
                 navigationIcon = {},
@@ -419,7 +420,6 @@
  * @param modifier a [Modifier]
  * @param titleAlpha the title's alpha
  * @param titleVerticalArrangement the title's vertical arrangement
- * @param titleHorizontalArrangement the title's horizontal arrangement
  * @param titleBottomPadding the title's bottom padding
  * @param hideTitleSemantics hides the title node from the semantic tree. Apply this
  * boolean when this layout is part of a [TwoRowsTopAppBar] to hide the title's semantics
@@ -440,7 +440,6 @@
     titleTextStyle: TextStyle,
     titleAlpha: Float,
     titleVerticalArrangement: Arrangement.Vertical,
-    titleHorizontalArrangement: Arrangement.Horizontal,
     titleBottomPadding: Int,
     hideTitleSemantics: Boolean,
     navigationIcon: @Composable () -> Unit,
@@ -470,10 +469,10 @@
                     CompositionLocalProvider(
                         LocalContentColor provides titleContentColor,
                         LocalDensity provides with(LocalDensity.current) {
-                          Density(
-                              density = density,
-                              fontScale = if (titleScaleDisabled) 1f else fontScale,
-                          )
+                            Density(
+                                density = density,
+                                fontScale = if (titleScaleDisabled) 1f else fontScale,
+                            )
                         },
                         content = title
                     )
@@ -528,15 +527,7 @@
 
             // Title
             titlePlaceable.placeRelative(
-                x = when (titleHorizontalArrangement) {
-                    Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2
-                    Arrangement.End ->
-                        constraints.maxWidth - titlePlaceable.width - actionIconsPlaceable.width
-                    // Arrangement.Start.
-                    // A TopAppBarTitleInset will make sure the title is offset in case the
-                    // navigation icon is missing.
-                    else -> max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width)
-                },
+                x = max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width),
                 y = when (titleVerticalArrangement) {
                     Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
                     // Apply bottom padding from the title's baseline only when the Arrangement is
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
index 09f5945..bc67e4c 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.spa.widget.editor
 
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -40,13 +41,13 @@
     @Test
     fun exposedDropdownMenuBoxs_displayed() {
         composeTestRule.setContent {
-            var selectedItem by remember { mutableStateOf("item1") }
+            var selectedItem by remember { mutableStateOf(0) }
             SettingsExposedDropdownMenuBox(
                 label = exposedDropdownMenuBoxLabel,
                 options = options,
-                selectedOptionText = selectedItem,
+                selectedOptionIndex = selectedItem,
                 enabled = true,
-                onselectedOptionTextChange = {selectedItem = it})
+                onselectedOptionTextChange = { selectedItem = it })
         }
         composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
             .assertIsDisplayed()
@@ -55,13 +56,13 @@
     @Test
     fun exposedDropdownMenuBoxs_expanded() {
         composeTestRule.setContent {
-            var selectedItem by remember { mutableStateOf("item1") }
+            var selectedItem by remember { mutableIntStateOf(0) }
             SettingsExposedDropdownMenuBox(
                 label = exposedDropdownMenuBoxLabel,
                 options = options,
-                selectedOptionText = selectedItem,
+                selectedOptionIndex = selectedItem,
                 enabled = true,
-                onselectedOptionTextChange = {selectedItem = it})
+                onselectedOptionTextChange = { selectedItem = it })
         }
         composeTestRule.onNodeWithText(item2, substring = true)
             .assertDoesNotExist()
@@ -74,13 +75,13 @@
     @Test
     fun exposedDropdownMenuBoxs_valueChanged() {
         composeTestRule.setContent {
-            var selectedItem by remember { mutableStateOf("item1") }
+            var selectedItem by remember { mutableIntStateOf(0) }
             SettingsExposedDropdownMenuBox(
                 label = exposedDropdownMenuBoxLabel,
                 options = options,
-                selectedOptionText = selectedItem,
+                selectedOptionIndex = selectedItem,
                 enabled = true,
-                onselectedOptionTextChange = {selectedItem = it})
+                onselectedOptionTextChange = { selectedItem = it })
         }
         composeTestRule.onNodeWithText(item2, substring = true)
             .assertDoesNotExist()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
index 58bc722..b0271ae1 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
@@ -45,11 +45,12 @@
     @Test
     fun exposedDropdownMenuCheckBox_displayed() {
         composeTestRule.setContent {
-            SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+            SettingsExposedDropdownMenuCheckBox(
+                label = exposedDropdownMenuCheckBoxLabel,
                 options = options,
                 selectedOptionsState = remember { selectedOptionsState1 },
                 enabled = true,
-                onselectedOptionStateChange = {})
+            ) {}
         }
         composeTestRule.onNodeWithText(
             exposedDropdownMenuCheckBoxLabel, substring = true
@@ -59,11 +60,12 @@
     @Test
     fun exposedDropdownMenuCheckBox_expanded() {
         composeTestRule.setContent {
-            SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+            SettingsExposedDropdownMenuCheckBox(
+                label = exposedDropdownMenuCheckBoxLabel,
                 options = options,
                 selectedOptionsState = remember { selectedOptionsState1 },
                 enabled = true,
-                onselectedOptionStateChange = {})
+            ) {}
         }
         composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
         composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
@@ -74,11 +76,12 @@
     @Test
     fun exposedDropdownMenuCheckBox_valueAdded() {
         composeTestRule.setContent {
-            SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+            SettingsExposedDropdownMenuCheckBox(
+                label = exposedDropdownMenuCheckBoxLabel,
                 options = options,
                 selectedOptionsState = remember { selectedOptionsState1 },
                 enabled = true,
-                onselectedOptionStateChange = {})
+            ) {}
         }
         composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
         composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
@@ -90,11 +93,12 @@
     @Test
     fun exposedDropdownMenuCheckBox_valueDeleted() {
         composeTestRule.setContent {
-            SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+            SettingsExposedDropdownMenuCheckBox(
+                label = exposedDropdownMenuCheckBoxLabel,
                 options = options,
                 selectedOptionsState = remember { selectedOptionsState1 },
                 enabled = true,
-                onselectedOptionStateChange = {})
+            ) {}
         }
         composeTestRule.onNodeWithText(item2, substring = true).assertIsDisplayed()
         composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt
new file mode 100644
index 0000000..6f2d1f9
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPasswordTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertTextContains
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTextReplacement
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsTextFieldPasswordTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+    private val label = "label"
+    private val value = "value"
+    private val valueChanged = "Value Changed"
+    private val visibilityIconTag = "Visibility Icon"
+
+    @Test
+    fun textFieldPassword_displayed() {
+        composeTestRule.setContent {
+            SettingsTextFieldPassword(
+                value = value,
+                label = label,
+                onTextChange = {})
+        }
+        composeTestRule.onNodeWithText(label, substring = true)
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun textFieldPassword_invisible() {
+        composeTestRule.setContent {
+            var value by remember { mutableStateOf(value) }
+            SettingsTextFieldPassword(
+                value = value,
+                label = label,
+                onTextChange = { value = it })
+        }
+        composeTestRule.onNodeWithText(value, substring = true)
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun textFieldPassword_visible_inputValue() {
+        composeTestRule.setContent {
+            var value by remember { mutableStateOf(value) }
+            SettingsTextFieldPassword(
+                value = value,
+                label = label,
+                onTextChange = { value = it })
+        }
+        composeTestRule.onNodeWithTag(visibilityIconTag)
+            .performClick()
+        composeTestRule.onNodeWithText(label, substring = true)
+            .performTextReplacement(valueChanged)
+        composeTestRule.onNodeWithText(label, substring = true)
+            .assertTextContains(valueChanged)
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
index a6a5ed22..3dac7db 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material3.CenterAlignedTopAppBar
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
@@ -307,8 +306,8 @@
     }
 
     /**
-     * Checks the app bar's components positioning when it's a [CustomizedTopAppBar], a
-     * [CenterAlignedTopAppBar], or a larger app bar that is scrolled up and collapsed into a small
+     * Checks the app bar's components positioning when it's a [CustomizedTopAppBar]
+     * or a larger app bar that is scrolled up and collapsed into a small
      * configuration and there is no navigation icon.
      */
     private fun assertSmallPositioningWithoutNavigation(isCenteredTitle: Boolean = false) {
@@ -335,8 +334,7 @@
     }
 
     /**
-     * Checks the app bar's components positioning when it's a [CustomizedTopAppBar] or a
-     * [CenterAlignedTopAppBar].
+     * Checks the app bar's components positioning when it's a [CustomizedTopAppBar].
      */
     private fun assertSmallDefaultPositioning(isCenteredTitle: Boolean = false) {
         val appBarBounds = rule.onNodeWithTag(TopAppBarTestTag).getUnclippedBoundsInRoot()
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8dc95f4..f9ea773 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1254,7 +1254,6 @@
     <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
     <string name="wifi_tether_connected_summary">
         {count, plural,
-            =0    {0 device connected}
             =1    {1 device connected}
             other {# devices connected}
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 91b852a..70126ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -198,19 +198,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
     boolean isA2dpPlaying() {
         if (mService == null) return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index c7a5bd8..38cd04e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -140,19 +140,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     boolean isAudioPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
index c3f845c..45cafc6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -178,19 +178,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
index 6b7f733..660090d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
@@ -258,19 +258,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 7e5c124..b79f147 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -165,19 +165,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 5fbb4c3..14fab16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -231,19 +231,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 66225a2..b0e0049 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -150,19 +150,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 8a2c4f8..5468efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -123,13 +123,13 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         // if set preferred to false, then disconnect to the current device
         if (!enabled) {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 3c24b4a..5b91ac9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -126,19 +126,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index e781f13..a93524f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -24,24 +24,18 @@
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothCodecConfig;
-import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.os.Build;
-import android.os.ParcelUuid;
 import android.util.Log;
 
 import androidx.annotation.RequiresApi;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 public class LeAudioProfile implements LocalBluetoothProfile {
@@ -233,19 +227,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 4881a86..0a803bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -137,19 +137,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 75c1926..db1ba5e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -138,19 +138,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 767df35..dec862b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -105,7 +105,7 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
@@ -117,12 +117,12 @@
                     mService.setConnectionPolicy(sink, CONNECTION_POLICY_FORBIDDEN);
                 }
             }
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 0d11293..65b89ba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -151,19 +151,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 9e2e4a1..784b821 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -108,16 +108,16 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
 
         if (!enabled) {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 104f1d7..0f8892f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -136,19 +136,19 @@
 
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
-        boolean isEnabled = false;
+        boolean isSuccessful = false;
         if (mService == null) {
             return false;
         }
         if (enabled) {
             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
-                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+                isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+            isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
         }
 
-        return isEnabled;
+        return isSuccessful;
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
deleted file mode 100644
index 54d5c3d..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.net;
-
-import android.app.usage.NetworkStats;
-import android.app.usage.NetworkStatsManager;
-import android.content.Context;
-import android.net.NetworkTemplate;
-import android.util.Log;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-/**
- * Loader for retrieving the network stats summary for all UIDs.
- */
-public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
-
-    private static final String TAG = "NetworkDetailLoader";
-    private final NetworkStatsManager mNetworkStatsManager;
-    private final long mStart;
-    private final long mEnd;
-    private final NetworkTemplate mNetworkTemplate;
-
-    private NetworkStatsSummaryLoader(Builder builder) {
-        super(builder.mContext);
-        mStart = builder.mStart;
-        mEnd = builder.mEnd;
-        mNetworkTemplate = builder.mNetworkTemplate;
-        mNetworkStatsManager = (NetworkStatsManager)
-                builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
-    }
-
-    @Override
-    protected void onStartLoading() {
-        super.onStartLoading();
-        forceLoad();
-    }
-
-    @Override
-    public NetworkStats loadInBackground() {
-        try {
-            return mNetworkStatsManager.querySummary(mNetworkTemplate, mStart, mEnd);
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Exception querying network detail.", e);
-            return null;
-        }
-    }
-
-    @Override
-    protected void onStopLoading() {
-        super.onStopLoading();
-        cancelLoad();
-    }
-
-    @Override
-    protected void onReset() {
-        super.onReset();
-        cancelLoad();
-    }
-
-    public static class Builder {
-        private final Context mContext;
-        private long mStart;
-        private long mEnd;
-        private NetworkTemplate mNetworkTemplate;
-
-        public Builder(Context context) {
-            mContext = context;
-        }
-
-        public Builder setStartTime(long start) {
-            mStart = start;
-            return this;
-        }
-
-        public Builder setEndTime(long end) {
-            mEnd = end;
-            return this;
-        }
-
-        /**
-         * Set {@link NetworkTemplate} for builder
-         */
-        public Builder setNetworkTemplate(NetworkTemplate template) {
-            mNetworkTemplate = template;
-            return this;
-        }
-
-        public NetworkStatsSummaryLoader build() {
-            return new NetworkStatsSummaryLoader(this);
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
index 6fba0a1..77789c2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
@@ -24,5 +24,5 @@
     public CharSequence[] detailLabels;
     public CharSequence[] detailContentDescriptions;
     public Drawable icon;
-    public CharSequence packageName;
+    public String packageName;
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ffe28a6..15620b7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
         >
 
         <!-- Standard permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER" />
     <uses-permission android:name="android.permission.MANAGE_HEALTH_PERMISSIONS" />
     <uses-permission android:name="android.permission.MANAGE_HEALTH_DATA" />
     <uses-permission android:name="android.permission.health.READ_EXERCISE_ROUTE" />
@@ -852,6 +853,9 @@
     <!-- Permission required for accessing all content provider mime types -->
     <uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />
 
+    <!-- Permission required for CTS-in-sandbox tests -->
+    <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX" />
+
     <!-- Permission required for CTS test - CtsWallpaperTestCases -->
     <uses-permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER" />
 
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 77925d6..6080101 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -203,6 +203,9 @@
         "LowLightDreamLib",
         "motion_tool_lib",
     ],
+    libs: [
+        "keepanno-annotations",
+    ],
     manifest: "AndroidManifest.xml",
 
     javacflags: ["-Adagger.fastInit=enabled"],
@@ -466,6 +469,7 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "keepanno-annotations",
     ],
     kotlincflags: ["-Xjvm-default=all"],
     aaptflags: [
@@ -497,6 +501,9 @@
     static_libs: [
         "SystemUI-tests-base",
     ],
+    libs: [
+        "keepanno-annotations",
+    ],
     aaptflags: [
         "--extra-packages",
         "com.android.systemui",
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 78da5a6..8f329b3 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -4,7 +4,6 @@
 
 dsandler@android.com
 
-aaliomer@google.com
 aaronjli@google.com
 achalke@google.com
 acul@google.com
@@ -36,6 +35,7 @@
 gwasserman@google.com
 hwwang@google.com
 hyunyoungs@google.com
+ikateryna@google.com
 jaggies@google.com
 jamesoleary@google.com
 jbolinger@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index cb9e9ee..af6fa86 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -43,7 +43,9 @@
         {
           "exclude-annotation": "android.platform.test.annotations.Postsubmit"
         }
-      ]
+      ],
+      // The test doesn't run on AOSP Cuttlefish
+      "keywords": ["internal"]
     },
     {
       // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
@@ -55,7 +57,9 @@
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
-      ]
+      ],
+      // The test doesn't run on AOSP Cuttlefish
+      "keywords": ["internal"]
     },
     {
       // Permission indicators
@@ -112,10 +116,8 @@
           "include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"
         }
       ]
-    }
-  ],
-  "postsubmit": [
-   {
+    },
+    {
       "name": "SystemUIGoogleScreenshotTests",
       "options": [
         {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index 03cbc16..0f55f35 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -5,4 +5,11 @@
     namespace: "accessibility"
     description: "Provides/restores back button functionality for the a11yMenu settings page. Also, fixes sizing problems with large shortcut buttons."
     bug: "298467628"
+}
+
+flag {
+    name: "a11y_menu_hide_before_taking_action"
+    namespace: "accessibility"
+    description: "Hides the AccessibilityMenuService UI before taking action instead of after."
+    bug: "292020123"
 }
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 587395d..fd9a9c6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -15,7 +15,7 @@
       android:layout_centerHorizontal="true"
       android:scaleType="fitCenter"/>
 
-<TextView
+  <TextView
       android:id="@+id/shortcutLabel"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
@@ -27,6 +27,6 @@
       android:lines="2"
       android:textSize="@dimen/label_text_size"
       android:textAlignment="center"
-      android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.Button"/>
+      android:textColor="@color/grid_item_text_color"/>
 
 </RelativeLayout>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
index fbf2b07..f961bdb 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
@@ -20,5 +20,6 @@
   <color name="footer_icon_disabled_color">#5F6368</color>
   <color name="colorControlNormal">#202124</color>
   <color name="snackbar_bg_color">@android:color/white</color>
+  <color name="grid_item_text_color">@android:color/white</color>
 
 </resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
index d81d0d5..9722eb0 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
@@ -21,6 +21,7 @@
   <color name="footer_icon_disabled_color">#ddd</color>
   <color name="colorControlNormal">@android:color/white</color>
   <color name="snackbar_bg_color">#313235</color>
+  <color name="grid_item_text_color">@android:color/black</color>
 
   <color name="colorAccessibilityMenuIcon">#3AA757</color>
 </resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 27aade5..008732e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -260,6 +260,27 @@
         // Shortcuts are repeatable in a11y menu rather than unique, so use tag ID to handle.
         int viewTag = (int) view.getTag();
 
+        // First check if this was a shortcut which should keep a11y menu visible. If so,
+        // perform the shortcut and return without hiding the UI.
+        if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {
+            adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);
+            return;
+        } else if (viewTag == ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()) {
+            adjustBrightness(BRIGHTNESS_DOWN_INCREMENT_GAMMA);
+            return;
+        } else if (viewTag == ShortcutId.ID_VOLUME_UP_VALUE.ordinal()) {
+            adjustVolume(AudioManager.ADJUST_RAISE);
+            return;
+        } else if (viewTag == ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()) {
+            adjustVolume(AudioManager.ADJUST_LOWER);
+            return;
+        }
+
+        if (Flags.a11yMenuHideBeforeTakingAction()) {
+            // Hide the a11y menu UI before performing the following shortcut actions.
+            mA11yMenuLayout.hideMenu();
+        }
+
         if (viewTag == ShortcutId.ID_ASSISTANT_VALUE.ordinal()) {
             // Always restart the voice command activity, so that the UI is reloaded.
             startActivityIfIntentIsSafe(
@@ -281,21 +302,11 @@
             performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS);
         } else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) {
             performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
-        } else if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {
-            adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);
-            return;
-        } else if (viewTag == ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()) {
-            adjustBrightness(BRIGHTNESS_DOWN_INCREMENT_GAMMA);
-            return;
-        } else if (viewTag == ShortcutId.ID_VOLUME_UP_VALUE.ordinal()) {
-            adjustVolume(AudioManager.ADJUST_RAISE);
-            return;
-        } else if (viewTag == ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal()) {
-            adjustVolume(AudioManager.ADJUST_LOWER);
-            return;
         }
 
-        mA11yMenuLayout.hideMenu();
+        if (!Flags.a11yMenuHideBeforeTakingAction()) {
+            mA11yMenuLayout.hideMenu();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 0c07616..1b56d4a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -605,12 +605,28 @@
                 object : Controller by delegate {
                     override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
                         listener?.onLaunchAnimationStart()
+
+                        if (DEBUG_LAUNCH_ANIMATION) {
+                            Log.d(
+                                TAG,
+                                "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" +
+                                    "$isExpandingFullyAbove) [controller=$delegate]"
+                            )
+                        }
                         delegate.onLaunchAnimationStart(isExpandingFullyAbove)
                     }
 
                     override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
                         listener?.onLaunchAnimationEnd()
                         iCallback?.invoke()
+
+                        if (DEBUG_LAUNCH_ANIMATION) {
+                            Log.d(
+                                TAG,
+                                "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" +
+                                    "$isExpandingFullyAbove) [controller=$delegate]"
+                            )
+                        }
                         delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
                     }
 
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 767756e..17c74ba 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -263,9 +263,11 @@
             override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
                 delegate.onLaunchAnimationStart(isExpandingFullyAbove)
                 overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
+                cujType?.let { InteractionJankMonitor.getInstance().begin(composeViewRoot, it) }
             }
 
             override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+                cujType?.let { InteractionJankMonitor.getInstance().end(it) }
                 delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
                 overlay.value = null
             }
@@ -320,9 +322,8 @@
             }
 
             override fun jankConfigurationBuilder(): InteractionJankMonitor.Configuration.Builder? {
-                // TODO(b/252723237): Add support for jank monitoring when animating from a
-                // Composable.
-                return null
+                val type = cuj?.cujType ?: return null
+                return InteractionJankMonitor.Configuration.Builder.withView(type, composeViewRoot)
             }
         }
     }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
index 7536728..88944f10 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -107,6 +107,9 @@
     reversed: Boolean = false,
 ) {
     val fromScene = layoutImpl.state.transitionState.currentScene
+    val isUserInput =
+        (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
+            ?: false
 
     val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
     val visibilityThreshold =
@@ -116,9 +119,9 @@
     val targetProgress = if (reversed) 0f else 1f
     val transition =
         if (reversed) {
-            OneOffTransition(target, fromScene, currentScene = target, animatable)
+            OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
         } else {
-            OneOffTransition(fromScene, target, currentScene = target, animatable)
+            OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
         }
 
     // Change the current layout state to use this new transition.
@@ -139,6 +142,7 @@
     override val fromScene: SceneKey,
     override val toScene: SceneKey,
     override val currentScene: SceneKey,
+    override val isUserInputDriven: Boolean,
     private val animatable: Animatable<Float, AnimationVector1D>,
 ) : TransitionState.Transition {
     override val progress: Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index a625250..ccdec6e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -42,6 +42,17 @@
         val fromScene: SceneKey,
         val toScene: SceneKey,
         val progress: Flow<Float>,
+
+        /**
+         * Whether the transition was originally triggered by user input rather than being
+         * programmatic. If this value is initially true, it will remain true until the transition
+         * fully completes, even if the user input that triggered the transition has ended. Any
+         * sub-transitions launched by this one will inherit this value. For example, if the user
+         * drags a pointer but does not exceed the threshold required to transition to another
+         * scene, this value will remain true after the pointer is no longer touching the screen and
+         * will be true in any transition created to animate back to the original position.
+         */
+        val isUserInputDriven: Boolean,
     ) : ObservableTransitionState()
 }
 
@@ -62,6 +73,7 @@
                             fromScene = state.fromScene,
                             toScene = state.toScene,
                             progress = snapshotFlow { state.progress },
+                            isUserInputDriven = state.isUserInputDriven,
                         )
                     }
                 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 47e3d5a..7a21211 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -68,5 +68,8 @@
          * when flinging quickly during a swipe gesture.
          */
         val progress: Float
+
+        /** Whether the transition was triggered by user input rather than being programmatic. */
+        val isUserInputDriven: Boolean
     }
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
index 2069ebd..790ea08 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -137,6 +137,8 @@
             return offset / distance
         }
 
+    override val isUserInputDriven = true
+
     /** The current offset caused by the drag gesture. */
     var dragOffset by mutableFloatStateOf(0f)
 
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index cb2607a..2232370 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -122,6 +122,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
+        assertThat(transition.isUserInputDriven).isTrue()
 
         // Release the finger. We should now be animating back to A (currentScene = SceneA) given
         // that 55dp < positional threshold.
@@ -133,6 +134,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
+        assertThat(transition.isUserInputDriven).isTrue()
 
         // Wait for the animation to finish. We should now be in scene A.
         rule.waitForIdle()
@@ -154,6 +156,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
+        assertThat(transition.isUserInputDriven).isTrue()
 
         // Release the finger. We should now be animating to C (currentScene = SceneC) given
         // that 56dp >= positional threshold.
@@ -165,6 +168,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
+        assertThat(transition.isUserInputDriven).isTrue()
 
         // Wait for the animation to finish. We should now be in scene C.
         rule.waitForIdle()
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
new file mode 100644
index 0000000..f80a906
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.scene
+
+import dagger.Module
+
+@Module interface CommunalSceneModule
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
new file mode 100644
index 0000000..94b5db2
--- /dev/null
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.scene
+
+import com.android.systemui.communal.ui.compose.CommunalScene
+import com.android.systemui.scene.shared.model.Scene
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface CommunalSceneModule {
+    @Binds @IntoSet fun communalScene(scene: CommunalScene): Scene
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index e06a69b..5505eaf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -82,7 +82,7 @@
 ) : ComposableScene {
     override val key = SceneKey.Bouncer
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow(
                 mapOf(
                     UserAction.Back to SceneModel(SceneKey.Lockscreen),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
new file mode 100644
index 0000000..0d2ba28
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.communal.ui.compose
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.scene.shared.model.UserAction
+import com.android.systemui.scene.ui.composable.ComposableScene
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** The communal scene shows glanceable hub when the device is locked and docked. */
+@SysUISingleton
+class CommunalScene @Inject constructor() : ComposableScene {
+    override val key = SceneKey.Communal
+
+    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+        MutableStateFlow<Map<UserAction, SceneModel>>(
+                mapOf(
+                    UserAction.Swipe(Direction.RIGHT) to SceneModel(SceneKey.Lockscreen),
+                )
+            )
+            .asStateFlow()
+
+    @Composable
+    override fun SceneScope.Content(modifier: Modifier) {
+        Box(
+            modifier = modifier.fillMaxSize().background(Color.White),
+        ) {
+            Text(
+                modifier = Modifier.align(Alignment.Center),
+                text = "Hello Communal!",
+            )
+        }
+    }
+}
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 463253b..77daebb 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
@@ -66,13 +66,19 @@
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
         viewModel.upDestinationSceneKey
-            .map { pageKey -> destinationScenes(up = pageKey) }
+            .map { pageKey ->
+                destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey)
+            }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
-                initialValue = destinationScenes(up = null)
+                initialValue =
+                    destinationScenes(
+                        up = viewModel.upDestinationSceneKey.value,
+                        left = viewModel.leftDestinationSceneKey,
+                    )
             )
 
     @Composable
@@ -88,9 +94,11 @@
 
     private fun destinationScenes(
         up: SceneKey?,
+        left: SceneKey?,
     ): Map<UserAction, SceneModel> {
         return buildMap {
             up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) }
+            left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) }
             this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade)
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 7ac3901..1f9c3e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -58,13 +58,14 @@
 ) : ComposableScene {
     override val key = SceneKey.QuickSettings
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    private val _destinationScenes =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
                     UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
                 )
             )
             .asStateFlow()
+    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = _destinationScenes
 
     @Composable
     override fun SceneScope.Content(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 40b0b4a..2ee461f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -38,7 +38,7 @@
 class GoneScene @Inject constructor() : ComposableScene {
     override val key = SceneKey.Gone
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
                     UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 6a5a368..ef01266 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -79,7 +79,7 @@
     val currentSceneKey = currentSceneModel.key
     val currentScene = checkNotNull(sceneByKey[currentSceneKey])
     val currentDestinations: Map<UserAction, SceneModel> by
-        currentScene.destinationScenes().collectAsState()
+        currentScene.destinationScenes.collectAsState()
     val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) }
     val isRibbonEnabled = remember { SystemProperties.getBoolean("flexi.ribbon", false) }
 
@@ -116,7 +116,7 @@
                         if (sceneKey == currentSceneKey) {
                                 currentDestinations
                             } else {
-                                composableScene.destinationScenes().value
+                                composableScene.destinationScenes.value
                             }
                             .map { (userAction, destinationSceneModel) ->
                                 toTransitionModels(userAction, destinationSceneModel)
@@ -158,6 +158,7 @@
                 fromScene = fromScene.toModel().key,
                 toScene = toScene.toModel().key,
                 progress = progress,
+                isUserInputDriven = isUserInputDriven,
             )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 404bf81..2848245 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -5,6 +5,7 @@
 import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToBouncerTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToCommunalTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
@@ -13,7 +14,7 @@
 /**
  * Comprehensive definition of all transitions between scenes in [SceneContainer].
  *
- * Transitions are automatically reversible, so define only one transition per scene pair. By
+ * Transitions are automatically reversible, so define only one transition per scene pair. By\
  * convention, use the more common transition direction when defining the pair order, e.g.
  * Lockscreen to Bouncer rather than Bouncer to Lockscreen.
  *
@@ -27,6 +28,7 @@
     from(Gone, to = Shade) { goneToShadeTransition() }
     from(Gone, to = QuickSettings) { goneToQuickSettingsTransition() }
     from(Lockscreen, to = Bouncer) { lockscreenToBouncerTransition() }
+    from(Lockscreen, to = Communal) { lockscreenToCommunalTransition() }
     from(Lockscreen, to = Shade) { lockscreenToShadeTransition() }
     from(Lockscreen, to = QuickSettings) { lockscreenToQuickSettingsTransition() }
     from(Lockscreen, to = Gone) { lockscreenToGoneTransition() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
index 8d0d705..5336bf6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
@@ -8,6 +8,7 @@
 val Shade = SceneKey.Shade.toTransitionSceneKey()
 val QuickSettings = SceneKey.QuickSettings.toTransitionSceneKey()
 val Gone = SceneKey.Gone.toTransitionSceneKey()
+val Communal = SceneKey.Communal.toTransitionSceneKey()
 
 // TODO(b/293899074): Remove this file once we can use the scene keys from SceneTransitionLayout.
 fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
new file mode 100644
index 0000000..ea8110a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.Communal
+import com.android.systemui.scene.ui.composable.Lockscreen
+
+fun TransitionBuilder.lockscreenToCommunalTransition() {
+    spec = tween(durationMillis = 500)
+
+    // Translate lockscreen to the left.
+    translate(Lockscreen.rootElementKey, Edge.Left)
+
+    // Translate communal from the right.
+    translate(Communal.rootElementKey, Edge.Right)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index b105637..8832a11 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -87,7 +87,7 @@
 ) : ComposableScene {
     override val key = SceneKey.Shade
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
         viewModel.upDestinationSceneKey
             .map { sceneKey -> destinationScenes(up = sceneKey) }
             .stateIn(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 92d2bd2..9d62e38 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -163,9 +163,6 @@
         const val TABLE_NAME = "flags"
         val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
 
-        /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
-        const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
-
         /**
          * Flag denoting whether the customizable lock screen quick affordances feature is enabled.
          */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
index cfe3be0..3ac6422 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
@@ -14,8 +14,6 @@
 
 package com.android.systemui.plugins.qs;
 
-import android.content.Context;
-
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.annotations.DependsOn;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -30,18 +28,7 @@
 public interface QSFactory extends Plugin {
 
     String ACTION = "com.android.systemui.action.PLUGIN_QS_FACTORY";
-    int VERSION = 2;
+    int VERSION = 3;
 
     QSTile createTile(String tileSpec);
-
-    /**
-     * Create a view for a tile.
-     *
-     * @param context a themed context for inflating the view
-     * @param tile the tile for which the view is created
-     * @param collapsedView {@code true} if the view will live in QQS and {@code false} otherwise.
-     * @return a view for the tile
-     */
-    QSTileView createTileView(Context context, QSTile tile, boolean collapsedView);
-
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 25f77ea..06e9b10 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -55,8 +55,6 @@
     void removeCallback(Callback callback);
     void removeCallbacks();
 
-    QSIconView createTileView(Context context);
-
     /**
      * The tile was clicked.
      *
@@ -170,7 +168,6 @@
         public boolean dualTarget = false;
         public boolean isTransient = false;
         public String expandedAccessibilityClassName;
-        public SlashState slash;
         public boolean handlesLongClick = true;
         @Nullable
         public Drawable sideViewCustomDrawable;
@@ -214,7 +211,6 @@
                     || !Objects.equals(other.state, state)
                     || !Objects.equals(other.isTransient, isTransient)
                     || !Objects.equals(other.dualTarget, dualTarget)
-                    || !Objects.equals(other.slash, slash)
                     || !Objects.equals(other.handlesLongClick, handlesLongClick)
                     || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
             other.spec = spec;
@@ -230,7 +226,6 @@
             other.state = state;
             other.dualTarget = dualTarget;
             other.isTransient = isTransient;
-            other.slash = slash != null ? slash.copy() : null;
             other.handlesLongClick = handlesLongClick;
             other.sideViewCustomDrawable = sideViewCustomDrawable;
             return changed;
@@ -258,7 +253,6 @@
             sb.append(",dualTarget=").append(dualTarget);
             sb.append(",isTransient=").append(isTransient);
             sb.append(",state=").append(state);
-            sb.append(",slash=\"").append(slash).append("\"");
             sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
             return sb.append(']');
         }
@@ -302,73 +296,4 @@
             return state;
         }
     }
-
-    @ProvidesInterface(version = SignalState.VERSION)
-    public static final class SignalState extends BooleanState {
-        public static final int VERSION = 1;
-        public boolean activityIn;
-        public boolean activityOut;
-        public boolean isOverlayIconWide;
-        public int overlayIconId;
-
-        @Override
-        public boolean copyTo(State other) {
-            final SignalState o = (SignalState) other;
-            final boolean changed = o.activityIn != activityIn
-                    || o.activityOut != activityOut
-                    || o.isOverlayIconWide != isOverlayIconWide
-                    || o.overlayIconId != overlayIconId;
-            o.activityIn = activityIn;
-            o.activityOut = activityOut;
-            o.isOverlayIconWide = isOverlayIconWide;
-            o.overlayIconId = overlayIconId;
-            return super.copyTo(other) || changed;
-        }
-
-        @Override
-        protected StringBuilder toStringBuilder() {
-            final StringBuilder rt = super.toStringBuilder();
-            rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
-            rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
-            return rt;
-        }
-
-        @Override
-        public State copy() {
-            SignalState state = new SignalState();
-            copyTo(state);
-            return state;
-        }
-    }
-
-    @ProvidesInterface(version = SlashState.VERSION)
-    public static class SlashState {
-        public static final int VERSION = 2;
-
-        public boolean isSlashed;
-        public float rotation;
-
-        @Override
-        public String toString() {
-            return "isSlashed=" + isSlashed + ",rotation=" + rotation;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == null) return false;
-            try {
-                return (((SlashState) o).rotation == rotation)
-                        && (((SlashState) o).isSlashed == isSlashed);
-            } catch (ClassCastException e) {
-                return false;
-            }
-        }
-
-        public SlashState copy() {
-            SlashState state = new SlashState();
-            state.rotation = rotation;
-            state.isSlashed = isSlashed;
-            return state;
-        }
-    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index b7088d5..02085b9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -124,13 +124,6 @@
         default void onDozeAmountChanged(float linear, float eased) {}
 
         /**
-         * Callback to be notified when the fullscreen or immersive state changes.
-         *
-         * @param isFullscreen if any of the system bar is hidden by the focused window.
-         */
-        default void onFullscreenStateChanged(boolean isFullscreen) {}
-
-        /**
          * Callback to be notified when the pulsing state changes
          */
         default void onPulsingChanged(boolean pulsing) {}
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index fcf1ed8..be1e655 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -1,13 +1,5 @@
 -keep class com.android.systemui.VendorServices
 
-# the `#inject` methods are accessed via reflection to work on ContentProviders
--keepclassmembers class * extends com.android.systemui.dagger.SysUIComponent { void inject(***); }
-
-# Needed for builds to properly initialize KeyFrames from xml scene
--keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
-  public <init>();
-}
-
 # Needed to ensure callback field references are kept in their respective
 # owning classes when the downstream callback registrars only store weak refs.
 # TODO(b/264686688): Handle these cases with more targeted annotations.
@@ -56,7 +48,6 @@
     public <init>(android.content.Context, android.util.AttributeSet);
 }
 
--keep class ** extends androidx.preference.PreferenceFragment
 -keep class com.android.systemui.tuner.*
 
 # The plugins and core log subpackages act as shared libraries that might be referenced in
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
new file mode 100644
index 0000000..173d57b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -0,0 +1,129 @@
+<?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.
+*/
+-->
+
+<!-- This file is needed when flag lockscreen.enable_landscape is on
+     Required for landscape lockscreen on small screens. -->
+<com.android.keyguard.KeyguardPasswordView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_password_view"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_horizontal|bottom"
+    android:gravity="bottom">
+
+    <!-- Layout here is visually identical to the previous keyguard_password_view.
+         I.E., 'constraints here effectively the same as the previous linear layout' -->
+    <androidx.constraintlayout.motion.widget.MotionLayout
+        android:id="@+id/password_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:maxWidth="@dimen/keyguard_security_width"
+        android:layout_gravity="center_horizontal"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical"
+        androidprv:layoutDescription="@xml/keyguard_password_scene">
+
+        <!-- Guideline need to align password right of centre,
+        when on small screen landscape layout -->
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/password_center_guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            androidprv:layout_constraintGuide_percent="0.5" />
+
+        <LinearLayout
+            android:id="@+id/keyguard_bouncer_message_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:layoutDirection="ltr"
+            android:orientation="vertical"
+            androidprv:layout_constraintTop_toTopOf="parent">
+
+            <include layout="@layout/keyguard_bouncer_message_area" />
+
+            <com.android.systemui.bouncer.ui.BouncerMessageView
+                android:id="@+id/bouncer_message_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" />
+
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/passwordEntry_container"
+            android:layout_width="280dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:theme="?attr/passwordStyle"
+            androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+            androidprv:layout_constraintEnd_toEndOf="parent"
+            androidprv:layout_constraintHorizontal_bias="0.5"
+            androidprv:layout_constraintStart_toStartOf="parent"
+            androidprv:layout_constraintTop_toBottomOf="@+id/keyguard_bouncer_message_container"
+            androidprv:layout_constraintVertical_bias="0.7777">
+
+            <EditText
+                android:id="@+id/passwordEntry"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:contentDescription="@string/keyguard_accessibility_password"
+                android:gravity="center_horizontal"
+                android:imeOptions="flagForceAscii|actionDone"
+                android:inputType="textPassword"
+                android:maxLength="500"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textCursorDrawable="@null"
+                android:textSize="16sp"
+                android:textStyle="normal" />
+
+            <ImageView
+                android:id="@+id/switch_ime_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="end|center_vertical"
+                android:layout_marginBottom="12dp"
+                android:background="?android:attr/selectableItemBackground"
+                android:clickable="true"
+                android:contentDescription="@string/accessibility_ime_switch_button"
+                android:padding="8dip"
+                android:src="@drawable/ic_lockscreen_ime"
+                android:tint="?android:attr/textColorPrimary"
+                android:visibility="gone" />
+        </FrameLayout>
+
+        <include
+            android:id="@+id/keyguard_selector_fade_container"
+            layout="@layout/keyguard_eca"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="12dp"
+            android:orientation="vertical"
+            androidprv:layout_constraintBottom_toBottomOf="parent" />
+
+    </androidx.constraintlayout.motion.widget.MotionLayout>
+
+</com.android.keyguard.KeyguardPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
new file mode 100644
index 0000000..b562d7b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -0,0 +1,109 @@
+<?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.
+*/
+-->
+
+<!-- This file is needed when flag lockscreen.enable_landscape is on
+     Required for landscape lockscreen on small screens. -->
+<com.android.keyguard.KeyguardPatternView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_pattern_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_horizontal|bottom"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="vertical">
+
+    <!-- Layout here is visually identical to the previous keyguard_pattern_view.
+             I.E., 'constraints here effectively the same as the previous linear layout' -->
+    <androidx.constraintlayout.motion.widget.MotionLayout
+        android:id="@+id/pattern_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_horizontal"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical"
+        android:maxWidth = "@dimen/biometric_auth_pattern_view_max_size"
+        androidprv:layoutDescription="@xml/keyguard_pattern_scene">
+
+        <!-- Guideline need to align pattern right of centre,
+        when on small screen landscape layout -->
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/pattern_center_guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            androidprv:layout_constraintGuide_percent="0.5" />
+
+        <LinearLayout
+            android:id="@+id/keyguard_bouncer_message_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:layoutDirection="ltr"
+            android:orientation="vertical"
+            androidprv:layout_constraintTop_toTopOf="parent">
+
+            <include layout="@layout/keyguard_bouncer_message_area" />
+
+            <com.android.systemui.bouncer.ui.BouncerMessageView
+                android:id="@+id/bouncer_message_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" />
+
+        </LinearLayout>
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/pattern_top_guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            androidprv:layout_constraintGuide_percent="0" />
+
+        <com.android.internal.widget.LockPatternView
+            android:id="@+id/lockPatternView"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginBottom="8dp"
+            androidprv:layout_constraintVertical_bias="1.0"
+            androidprv:layout_constraintDimensionRatio="1.0"
+            androidprv:layout_constraintStart_toStartOf="parent"
+            androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+            androidprv:layout_constraintEnd_toEndOf="parent"
+            androidprv:layout_constraintVertical_chainStyle="packed"
+            androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"/>
+
+        <include
+            android:id="@+id/keyguard_selector_fade_container"
+            layout="@layout/keyguard_eca"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+            android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+            android:orientation="vertical"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintTop_toBottomOf="@+id/lockPatternView" />
+
+    </androidx.constraintlayout.motion.widget.MotionLayout>
+
+</com.android.keyguard.KeyguardPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index 6835d59..6c79d5a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -34,6 +34,7 @@
         android:id="@+id/pin_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:maxWidth="@dimen/keyguard_security_width"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:layoutDirection="ltr"
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_password_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_password_scene.xml
new file mode 100644
index 0000000..092e10d
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_password_scene.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<MotionScene
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:motion="http://schemas.android.com/apk/res-auto"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto">
+
+    <Transition
+        motion:constraintSetStart="@id/single_constraints"
+        motion:constraintSetEnd="@+id/split_constraints"
+        motion:duration="0"
+        motion:autoTransition="none" />
+
+    <!-- No changes to default layout -->
+    <ConstraintSet android:id="@+id/single_constraints" />
+
+    <ConstraintSet android:id="@+id/split_constraints">
+
+        <Constraint
+            android:id="@+id/keyguard_bouncer_message_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintEnd_toStartOf="@+id/password_center_guideline"
+            androidprv:layout_constraintStart_toStartOf="parent"
+            androidprv:layout_constraintTop_toTopOf="parent"
+            androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"
+            androidprv:layout_constraintVertical_chainStyle="spread_inside" />
+        <Constraint
+            android:id="@+id/passwordEntry_container"
+            android:layout_width="280dp"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintVertical_bias="0.5"
+            androidprv:layout_constraintHorizontal_bias="0.5"
+            androidprv:layout_constraintEnd_toEndOf="parent"
+            androidprv:layout_constraintStart_toStartOf="@+id/password_center_guideline"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintTop_toTopOf="parent"/>
+        <Constraint
+            android:id="@+id/keyguard_selector_fade_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+            android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintEnd_toStartOf="@+id/password_center_guideline"
+            androidprv:layout_constraintStart_toStartOf="parent"
+            androidprv:layout_constraintTop_toBottomOf="@+id/keyguard_bouncer_message_container" />
+
+    </ConstraintSet>
+</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml
new file mode 100644
index 0000000..6112411
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_pattern_scene.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<MotionScene
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:motion="http://schemas.android.com/apk/res-auto"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto">
+
+    <Transition
+        motion:constraintSetStart="@id/single_constraints"
+        motion:constraintSetEnd="@+id/split_constraints"
+        motion:duration="0"
+        motion:autoTransition="none"/>
+
+    <!-- No changes to default layout -->
+    <ConstraintSet android:id="@+id/single_constraints"/>
+
+    <ConstraintSet android:id="@+id/split_constraints">
+
+        <Constraint
+            android:id="@+id/keyguard_bouncer_message_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintEnd_toStartOf="@+id/pattern_center_guideline"
+            androidprv:layout_constraintStart_toStartOf="parent"
+            androidprv:layout_constraintTop_toTopOf="parent" />
+        <Constraint
+            android:id="@+id/lockPatternView"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            androidprv:layout_constraintDimensionRatio="1.0"
+            androidprv:layout_constraintVertical_bias="0.5"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintEnd_toEndOf="parent"
+            androidprv:layout_constraintStart_toStartOf="@+id/pattern_center_guideline"
+            androidprv:layout_constraintTop_toTopOf="parent"
+            android:layout_marginBottom="0dp" />
+        <Constraint
+            android:id="@+id/keyguard_selector_fade_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintEnd_toStartOf="@+id/pattern_center_guideline"
+            androidprv:layout_constraintStart_toStartOf="parent"
+            android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+            android:layout_marginTop="@dimen/keyguard_eca_top_margin" />
+
+    </ConstraintSet>
+</MotionScene>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
index 44af9ef..2a1270c 100644
--- a/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
+++ b/packages/SystemUI/res-keyguard/xml/keyguard_pin_scene.xml
@@ -26,12 +26,10 @@
         motion:constraintSetStart="@id/single_constraints"
         motion:constraintSetEnd="@+id/split_constraints"
         motion:duration="0"
-        motion:autoTransition="none">
-    </Transition>
+        motion:autoTransition="none"/>
 
-    <ConstraintSet android:id="@+id/single_constraints">
-        <!-- No changes to default layout -->
-    </ConstraintSet>
+    <!-- No changes to default layout -->
+    <ConstraintSet android:id="@+id/single_constraints"/>
 
     <ConstraintSet android:id="@+id/split_constraints">
 
diff --git a/packages/SystemUI/res/color/notification_overlay_color.xml b/packages/SystemUI/res/color/notification_overlay_color.xml
new file mode 100644
index 0000000..c24bff9
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_overlay_color.xml
@@ -0,0 +1,23 @@
+<?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"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_pressed="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.15" />
+    <item android:state_hovered="true" android:color="?androidprv:attr/materialColorOnSurface" android:alpha="0.11" />
+    <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
deleted file mode 100644
index 56223db..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="6.0dp"
-        android:height="32dp"
-        android:viewportWidth="6.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorAccent">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
deleted file mode 100644
index 12c1a7a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="6.0dp"
-        android:height="32dp"
-        android:viewportWidth="6.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorAccent">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 61a8e8e..3eaa618 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License
   -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
         android:color="?android:attr/colorControlHighlight">
     <item>
@@ -23,4 +23,9 @@
             <solid android:color="?androidprv:attr/colorSurface" />
         </shape>
     </item>
-</ripple>
\ No newline at end of file
+    <item>
+        <shape>
+            <solid android:color="@color/notification_overlay_color" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 3a15ae4..beb481a 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -118,34 +118,38 @@
         app:layout_constraintStart_toEndOf="@id/date"
         app:layout_constraintTop_toTopOf="@id/clock" />
 
-    <LinearLayout
+    <FrameLayout
         android:id="@+id/shade_header_system_icons"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/shade_header_system_icons_height"
-        android:clickable="true"
-        android:orientation="horizontal"
-        android:gravity="center_vertical"
-        android:paddingStart="@dimen/shade_header_system_icons_padding_start"
-        android:paddingEnd="@dimen/shade_header_system_icons_padding_end"
-        android:paddingTop="@dimen/shade_header_system_icons_padding_top"
-        android:paddingBottom="@dimen/shade_header_system_icons_padding_bottom"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="@id/privacy_container"
         app:layout_constraintTop_toTopOf="@id/clock">
-
-        <com.android.systemui.statusbar.phone.StatusIconContainer
-            android:id="@+id/statusIcons"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="wrap_content"
-            android:paddingEnd="@dimen/signal_cluster_battery_padding" />
-
-        <com.android.systemui.battery.BatteryMeterView
-            android:id="@+id/batteryRemainingIcon"
+        <LinearLayout
+            android:id="@+id/hover_system_icons_container"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:textAppearance="@style/TextAppearance.QS.Status" />
-    </LinearLayout>
+            android:layout_height="match_parent"
+            android:layout_gravity="right|center_vertical"
+            android:gravity="center_vertical"
+            android:paddingStart="@dimen/hover_system_icons_container_padding_start"
+            android:paddingEnd="@dimen/hover_system_icons_container_padding_end"
+            android:paddingTop="@dimen/hover_system_icons_container_padding_top"
+            android:paddingBottom="@dimen/hover_system_icons_container_padding_bottom">
+
+            <com.android.systemui.statusbar.phone.StatusIconContainer
+                android:id="@+id/statusIcons"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+
+            <com.android.systemui.battery.BatteryMeterView
+                android:id="@+id/batteryRemainingIcon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:textAppearance="@style/TextAppearance.QS.Status" />
+        </LinearLayout>
+    </FrameLayout>
 
     <FrameLayout
         android:id="@+id/privacy_container"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index f6ce70d..dc560bf 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -150,19 +150,6 @@
                     android:layout_height="match_parent"
                     android:layout_weight="1"/>
 
-                <!-- Temporary entrypoint for the partial screensharing used for teamfooding -->
-                <!-- TODO(b/236838395) remove this and use redesigned dialog -->
-                <TextView
-                    android:id="@+id/button_app"
-                    android:visibility="gone"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="0"
-                    android:layout_gravity="end"
-                    android:layout_marginEnd="8dp"
-                    android:text="App"
-                    style="@style/Widget.Dialog.Button.BorderButton" />
-
                 <TextView
                     android:id="@+id/button_start"
                     android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 356b36f..fa7f9cf 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -30,12 +30,14 @@
     <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
         android:id="@+id/backgroundNormal"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent"
+        android:duplicateParentState="true"/>
 
     <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
         android:id="@+id/backgroundDimmed"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent"
+        android:duplicateParentState="true"/>
 
     <com.android.systemui.statusbar.notification.row.NotificationContentView
         android:id="@+id/expanded"
diff --git a/packages/SystemUI/res/values-land/bools.xml b/packages/SystemUI/res/values-land/bools.xml
new file mode 100644
index 0000000..e24792d
--- /dev/null
+++ b/packages/SystemUI/res/values-land/bools.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.
+*/
+-->
+<resources>
+    <!-- Only use small clock on lockscreen.
+     True here because only small clock used on small devices in landscape -->
+    <bool name="force_small_clock_on_lockscreen">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index d800d49..85fb3ac 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -42,4 +42,8 @@
     <!-- Whether we use large screen shade header which takes only one row compared to QS header -->
     <bool name="config_use_large_screen_shade_header">true</bool>
 
+    <!-- Whether to force split shade.
+     For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
+     TODO (b/293290851) - change this comment/resource when flag is enabled -->
+    <bool name="force_config_use_split_notification_shade">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 0667cd8..259b9ad 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -88,4 +88,7 @@
          overlaid -->
     <dimen name="global_actions_button_size">72dp</dimen>
     <dimen name="global_actions_button_padding">26dp</dimen>
+
+    <dimen name="keyguard_indication_margin_bottom">8dp</dimen>
+    <dimen name="lock_icon_margin_bottom">24dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/bools.xml b/packages/SystemUI/res/values-sw600dp-land/bools.xml
new file mode 100644
index 0000000..c4d77e8
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/bools.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.
+*/
+-->
+<resources>
+    <!-- Only use small clock on lockscreen.
+     False here because large clock is allowed on large devices in landscape -->
+    <bool name="force_small_clock_on_lockscreen">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 588638f..e63229a 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -36,4 +36,8 @@
     <integer name="power_menu_lite_max_columns">3</integer>
     <integer name="power_menu_lite_max_rows">2</integer>
 
+    <!-- Whether to force split shade.
+    For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
+    TODO (b/293290851) - change this comment/resource when flag is enabled -->
+    <bool name="force_config_use_split_notification_shade">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 915dcdb..1e54fc9 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -80,10 +80,10 @@
 
     <dimen name="large_screen_shade_header_height">42dp</dimen>
     <!-- start padding is smaller to account for status icon margins coming from drawable itself -->
-    <dimen name="shade_header_system_icons_padding_start">3dp</dimen>
-    <dimen name="shade_header_system_icons_padding_end">4dp</dimen>
-    <dimen name="shade_header_system_icons_padding_top">2dp</dimen>
-    <dimen name="shade_header_system_icons_padding_bottom">2dp</dimen>
+    <dimen name="hover_system_icons_container_padding_start">3dp</dimen>
+    <dimen name="hover_system_icons_container_padding_end">4dp</dimen>
+    <dimen name="hover_system_icons_container_padding_top">2dp</dimen>
+    <dimen name="hover_system_icons_container_padding_bottom">2dp</dimen>
 
     <!-- Lockscreen shade transition values -->
     <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
diff --git a/packages/SystemUI/res/values/bools.xml b/packages/SystemUI/res/values/bools.xml
index 91d3a88..3956662 100644
--- a/packages/SystemUI/res/values/bools.xml
+++ b/packages/SystemUI/res/values/bools.xml
@@ -59,4 +59,8 @@
 
       True here so bouncers constraints are updated when rotating on small screens -->
     <bool name="update_bouncer_constraints">true</bool>
+
+    <!-- Only use small clock on lockscreen.
+     False here because large clock used by default, unless otherwise specified -->
+    <bool name="force_small_clock_on_lockscreen">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c134806..c72f565 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -592,6 +592,11 @@
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">false</bool>
 
+    <!-- Whether to force split shade.
+    For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
+    TODO (b/293290851) - change this comment/resource when flag is enabled -->
+    <bool name="force_config_use_split_notification_shade">false</bool>
+
     <!-- Whether we use large screen shade header which takes only one row compared to QS header -->
     <bool name="config_use_large_screen_shade_header">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b086ed8..7e9d3f5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -496,10 +496,10 @@
     <dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
     <dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen>
     <dimen name="shade_header_system_icons_height">@dimen/large_screen_shade_header_min_height</dimen>
-    <dimen name="shade_header_system_icons_padding_start">0dp</dimen>
-    <dimen name="shade_header_system_icons_padding_end">0dp</dimen>
-    <dimen name="shade_header_system_icons_padding_top">0dp</dimen>
-    <dimen name="shade_header_system_icons_padding_bottom">0dp</dimen>
+    <dimen name="hover_system_icons_container_padding_start">0dp</dimen>
+    <dimen name="hover_system_icons_container_padding_end">0dp</dimen>
+    <dimen name="hover_system_icons_container_padding_top">0dp</dimen>
+    <dimen name="hover_system_icons_container_padding_bottom">0dp</dimen>
 
     <!-- The top margin of the panel that holds the list of notifications.
          On phones it's always 0dp but it's overridden in Car UI
@@ -873,12 +873,6 @@
          is displayed in the upper left corner. -->
     <dimen name="roaming_icon_start_padding">2sp</dimen>
 
-    <!-- Extra padding between the mobile data type icon and the strength indicator when the data
-         type icon is wide for the tile in quick settings. -->
-    <dimen name="wide_type_icon_start_padding_qs">3dp</dimen>
-
-    <dimen name="signal_indicator_to_icon_frame_spacing">3dp</dimen>
-
     <!-- Starting margin before the signal cluster -->
 
     <!-- Padding between signal cluster and battery icon -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index bd251bd..21696fe 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -50,7 +50,6 @@
     <item type="id" name="x_animator_tag_start_value"/>
     <item type="id" name="y_animator_tag_start_value"/>
     <item type="id" name="qs_icon_tag"/>
-    <item type="id" name="qs_slash_tag"/>
     <item type="id" name="scrim"/>
     <item type="id" name="scrim_alpha_start"/>
     <item type="id" name="scrim_alpha_end"/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 0c2341f..3cb5bc7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -59,9 +59,8 @@
      * Updates the matrix based on the provided parameters
      */
     public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
-            int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
-            int taskbarSize, boolean isLargeScreen,
-            int currentRotation, boolean isRtl) {
+            int canvasWidth, int canvasHeight, boolean isLargeScreen, int currentRotation,
+            boolean isRtl) {
         boolean isRotated = false;
         boolean isOrientationDifferent;
 
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 8200e5c..9059230 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
@@ -17,6 +17,7 @@
 package com.android.systemui.shared.rotation;
 
 import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
@@ -37,6 +38,8 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -67,6 +70,7 @@
 
 import java.io.PrintWriter;
 import java.util.Optional;
+import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
 /**
@@ -119,6 +123,8 @@
     private final int mIconCwStart0ResId;
     @DrawableRes
     private final int mIconCwStart90ResId;
+    /** Defaults to mainExecutor if not set via {@link #setBgExecutor(Executor)}. */
+    private Executor mBgExecutor;
 
     @DrawableRes
     private int mIconResId;
@@ -178,6 +184,8 @@
         mAccessibilityManager = AccessibilityManager.getInstance(context);
         mTaskStackListener = new TaskStackListenerImpl();
         mWindowRotationProvider = windowRotationProvider;
+
+        mBgExecutor = context.getMainExecutor();
     }
 
     public void setRotationButton(RotationButton rotationButton,
@@ -193,6 +201,10 @@
         return mContext;
     }
 
+    public void setBgExecutor(Executor bgExecutor) {
+        mBgExecutor = bgExecutor;
+    }
+
     /**
      * Called during Taskbar initialization.
      */
@@ -219,8 +231,11 @@
 
         mListenersRegistered = true;
 
-        updateDockedState(mContext.registerReceiver(mDockedReceiver,
-                new IntentFilter(Intent.ACTION_DOCK_EVENT)));
+        mBgExecutor.execute(() -> {
+            final Intent intent = mContext.registerReceiver(mDockedReceiver,
+                    new IntentFilter(Intent.ACTION_DOCK_EVENT));
+            mContext.getMainExecutor().execute(() -> updateDockedState(intent));
+        });
 
         if (registerRotationWatcher) {
             try {
@@ -246,11 +261,13 @@
 
         mListenersRegistered = false;
 
-        try {
-            mContext.unregisterReceiver(mDockedReceiver);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Docked receiver already unregistered", e);
-        }
+        mBgExecutor.execute(() -> {
+            try {
+                mContext.unregisterReceiver(mDockedReceiver);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Docked receiver already unregistered", e);
+            }
+        });
 
         if (mRotationWatcherRegistered) {
             try {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 1703b30..610df11 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -112,6 +112,8 @@
     private int mWeatherClockSmartspaceTranslateY = 0;
     private int mDrawAlpha = 255;
 
+    private int mStatusBarHeight = 0;
+
     /**
      * Maintain state so that a newly connected plugin can be initialized.
      */
@@ -154,6 +156,8 @@
                 R.dimen.weather_clock_smartspace_translateX);
         mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize(
                 R.dimen.weather_clock_smartspace_translateY);
+        mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.status_bar_height);
         updateStatusArea(/* animate= */false);
     }
 
@@ -299,6 +303,8 @@
         mStatusAreaAnim = null;
 
         View in, out;
+        // statusAreaYTranslation uses for the translation for both mStatusArea and mSmallClockFrame
+        // statusAreaClockTranslateY only uses for mStatusArea
         float statusAreaYTranslation, statusAreaClockScale = 1f;
         float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f;
         float clockInYTranslation, clockOutYTranslation;
@@ -313,10 +319,21 @@
                     && mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) {
                 statusAreaClockScale = mWeatherClockSmartspaceScaling;
                 statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX;
-                statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY - mSmartspaceTop;
                 if (mSplitShadeCentered) {
                     statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER;
                 }
+
+                // On large weather clock,
+                // top padding for time is status bar height from top of the screen.
+                // On small one,
+                // it's screenOffsetYPadding (translationY for KeyguardStatusView),
+                // Cause smartspace is positioned according to the smallClockFrame
+                // we need to translate the difference between bottom of large clock and small clock
+                // Also, we need to counter offset the empty date weather view, mSmartspaceTop
+                // mWeatherClockSmartspaceTranslateY is only for Felix
+                statusAreaClockTranslateY = mStatusBarHeight - 0.6F *  mSmallClockFrame.getHeight()
+                        - mSmartspaceTop - screenOffsetYPadding
+                        - statusAreaYTranslation + mWeatherClockSmartspaceTranslateY;
             }
             clockInYTranslation = 0;
             clockOutYTranslation = 0; // Small clock translation is handled with statusArea
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index bb799fc..d7019b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -27,7 +27,7 @@
     override var userId: Int = 0,
     override var listening: Boolean = false,
     // keep sorted
-    var allowedDisplayState: Boolean = false,
+    var allowedDisplayStateWhileAwake: Boolean = false,
     var alternateBouncerShowing: Boolean = false,
     var authInterruptActive: Boolean = false,
     var biometricSettingEnabledForUser: Boolean = false,
@@ -58,7 +58,7 @@
             userId.toString(),
             listening.toString(),
             // keep sorted
-            allowedDisplayState.toString(),
+            allowedDisplayStateWhileAwake.toString(),
             alternateBouncerShowing.toString(),
             authInterruptActive.toString(),
             biometricSettingEnabledForUser.toString(),
@@ -98,7 +98,7 @@
                 userId = model.userId
                 listening = model.listening
                 // keep sorted
-                allowedDisplayState = model.allowedDisplayState
+                allowedDisplayStateWhileAwake = model.allowedDisplayStateWhileAwake
                 alternateBouncerShowing = model.alternateBouncerShowing
                 authInterruptActive = model.authInterruptActive
                 biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
@@ -143,7 +143,7 @@
                 "userId",
                 "listening",
                 // keep sorted
-                "allowedDisplayState",
+                "allowedDisplayStateWhileAwake",
                 "alternateBouncerShowing",
                 "authInterruptActive",
                 "biometricSettingEnabledForUser",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 8738d33..abd1563 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,8 +16,6 @@
 
 package com.android.keyguard;
 
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -257,10 +255,8 @@
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
                         mFalsingCollector, mKeyguardViewController,
-                        mFeatureFlags);
+                        mDevicePostureController, mFeatureFlags);
             } else if (keyguardInputView instanceof KeyguardPINView) {
-                ((KeyguardPINView) keyguardInputView).setIsLockScreenLandscapeEnabled(
-                        mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index bb3e759..72b4ae5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
@@ -105,21 +106,21 @@
     }
 
     void onDevicePostureChanged(@DevicePostureInt int posture) {
-        if (mLastDevicePosture != posture) {
-            mLastDevicePosture = posture;
+        if (mLastDevicePosture == posture) return;
+        mLastDevicePosture = posture;
 
-            if (mIsLockScreenLandscapeEnabled) {
-                boolean useSplitBouncerAfterFold =
-                        mLastDevicePosture == DEVICE_POSTURE_CLOSED
-                        &&  getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+        if (mIsLockScreenLandscapeEnabled) {
+            boolean useSplitBouncerAfterFold =
+                    mLastDevicePosture == DEVICE_POSTURE_CLOSED
+                    && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+                    && getResources().getBoolean(R.bool.update_bouncer_constraints);
 
-                if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
-                    updateConstraints(useSplitBouncerAfterFold);
-                }
+            if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+                updateConstraints(useSplitBouncerAfterFold);
             }
-
-            updateMargins();
         }
+
+        updateMargins();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 59ee0d8..8d5fc04 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
@@ -27,10 +28,13 @@
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Insets;
@@ -46,12 +50,16 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.constraintlayout.motion.widget.MotionLayout;
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+
+
 /**
  * Displays an alphanumeric (latin-1) key entry for the user to enter
  * an unlock password
@@ -68,10 +76,14 @@
 
     private TextView mPasswordEntry;
     private TextViewInputDisabler mPasswordEntryDisabler;
-
     private Interpolator mLinearOutSlowInInterpolator;
     private Interpolator mFastOutLinearInInterpolator;
     private DisappearAnimationListener mDisappearAnimationListener;
+    @Nullable private MotionLayout mContainerMotionLayout;
+    private boolean mAlreadyUsingSplitBouncer = false;
+    private boolean mIsLockScreenLandscapeEnabled = false;
+    @DevicePostureController.DevicePostureInt
+    private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
     private static final int[] DISABLE_STATE_SET = {-android.R.attr.state_enabled};
     private static final int[] ENABLE_STATE_SET = {android.R.attr.state_enabled};
 
@@ -89,6 +101,21 @@
                 context, android.R.interpolator.fast_out_linear_in);
     }
 
+    /**
+     * Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is
+     * enabled
+     */
+    public void setIsLockScreenLandscapeEnabled() {
+        mIsLockScreenLandscapeEnabled = true;
+        findContainerLayout();
+    }
+
+    private void findContainerLayout() {
+        if (mIsLockScreenLandscapeEnabled) {
+            mContainerMotionLayout = findViewById(R.id.password_container);
+        }
+    }
+
     @Override
     protected void resetState() {
     }
@@ -124,6 +151,35 @@
         }
     }
 
+    void onDevicePostureChanged(@DevicePostureController.DevicePostureInt int posture) {
+        if (mLastDevicePosture == posture) return;
+        mLastDevicePosture = posture;
+
+        if (mIsLockScreenLandscapeEnabled) {
+            boolean useSplitBouncerAfterFold =
+                    mLastDevicePosture == DEVICE_POSTURE_CLOSED
+                    && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+                    && getResources().getBoolean(R.bool.update_bouncer_constraints);
+
+            if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+                updateConstraints(useSplitBouncerAfterFold);
+            }
+        }
+
+    }
+
+    @Override
+    protected void updateConstraints(boolean useSplitBouncer) {
+        mAlreadyUsingSplitBouncer = useSplitBouncer;
+        if (useSplitBouncer) {
+            mContainerMotionLayout.jumpToState(R.id.split_constraints);
+            mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
+        } else {
+            mContainerMotionLayout.jumpToState(R.id.single_constraints);
+            mContainerMotionLayout.setMaxWidth(getResources()
+                    .getDimensionPixelSize(R.dimen.keyguard_security_width));
+        }
+    }
 
     @Override
     protected void onFinishInflate() {
@@ -131,6 +187,11 @@
 
         mPasswordEntry = findViewById(getPasswordTextViewId());
         mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
+
+        // EditText cursor can fail screenshot tests, so disable it when testing
+        if (ActivityManager.isRunningInTestHarness()) {
+            mPasswordEntry.setCursorVisible(false);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 5dbd014..ab8cd53 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+
 import android.content.res.Resources;
 import android.os.UserHandle;
 import android.text.Editable;
@@ -41,6 +43,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.List;
@@ -49,8 +52,10 @@
         extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
 
     private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500;  // 500ms
-
     private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+    private final DevicePostureController mPostureController;
+    private final DevicePostureController.Callback mPostureCallback = posture ->
+            mView.onDevicePostureChanged(posture);
     private final InputMethodManager mInputMethodManager;
     private final DelayableExecutor mMainExecutor;
     private final KeyguardViewController mKeyguardViewController;
@@ -106,14 +111,19 @@
             @Main Resources resources,
             FalsingCollector falsingCollector,
             KeyguardViewController keyguardViewController,
+            DevicePostureController postureController,
             FeatureFlags featureFlags) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
                 emergencyButtonController, featureFlags);
         mKeyguardSecurityCallback = keyguardSecurityCallback;
         mInputMethodManager = inputMethodManager;
+        mPostureController = postureController;
         mMainExecutor = mainExecutor;
         mKeyguardViewController = keyguardViewController;
+        if (featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
+            view.setIsLockScreenLandscapeEnabled();
+        }
         mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
         mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
@@ -127,6 +137,9 @@
         mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
                 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
 
+        mView.onDevicePostureChanged(mPostureController.getDevicePosture());
+        mPostureController.addCallback(mPostureCallback);
+
         // Set selected property on so the view can send accessibility events.
         mPasswordEntry.setSelected(true);
         mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
@@ -164,6 +177,7 @@
     protected void onViewDetached() {
         super.onViewDetached();
         mPasswordEntry.setOnEditorActionListener(null);
+        mPostureController.removeCallback(mPostureCallback);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 2bdf46e..56706b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,9 +15,13 @@
  */
 package com.android.keyguard;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -29,6 +33,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
+import androidx.constraintlayout.motion.widget.MotionLayout;
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.constraintlayout.widget.ConstraintSet;
 
@@ -75,7 +80,10 @@
 
     BouncerKeyguardMessageArea mSecurityMessageDisplay;
     private View mEcaView;
-    private ConstraintLayout mContainer;
+    @Nullable private MotionLayout mContainerMotionLayout;
+    @Nullable private ConstraintLayout mContainerConstraintLayout;
+    private boolean mAlreadyUsingSplitBouncer = false;
+    private boolean mIsLockScreenLandscapeEnabled = false;
     @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
 
     public KeyguardPatternView(Context context) {
@@ -98,16 +106,44 @@
                 mContext, android.R.interpolator.fast_out_linear_in));
     }
 
+    /**
+     * Use motion layout (new bouncer implementation) if LOCKSCREEN_ENABLE_LANDSCAPE flag is
+     * enabled, instead of constraint layout (old bouncer implementation)
+     */
+    public void setIsLockScreenLandscapeEnabled(boolean isLockScreenLandscapeEnabled) {
+        mIsLockScreenLandscapeEnabled = isLockScreenLandscapeEnabled;
+        findContainerLayout();
+    }
+
+    private void findContainerLayout() {
+        if (mIsLockScreenLandscapeEnabled) {
+            mContainerMotionLayout = findViewById(R.id.pattern_container);
+        } else {
+            mContainerConstraintLayout = findViewById(R.id.pattern_container);
+        }
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         updateMargins();
     }
 
     void onDevicePostureChanged(@DevicePostureInt int posture) {
-        if (mLastDevicePosture != posture) {
-            mLastDevicePosture = posture;
-            updateMargins();
+        if (mLastDevicePosture == posture) return;
+        mLastDevicePosture = posture;
+
+        if (mIsLockScreenLandscapeEnabled) {
+            boolean useSplitBouncerAfterFold =
+                    mLastDevicePosture == DEVICE_POSTURE_CLOSED
+                    && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
+                    && getResources().getBoolean(R.bool.update_bouncer_constraints);
+
+            if (mAlreadyUsingSplitBouncer != useSplitBouncerAfterFold) {
+                updateConstraints(useSplitBouncerAfterFold);
+            }
         }
+
+        updateMargins();
     }
 
     private void updateMargins() {
@@ -115,12 +151,36 @@
         float halfOpenPercentage =
                 mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
 
-        ConstraintSet cs = new ConstraintSet();
-        cs.clone(mContainer);
-        cs.setGuidelinePercent(R.id.pattern_top_guideline,
-                mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED
-                ? halfOpenPercentage : 0.0f);
-        cs.applyTo(mContainer);
+        if (mIsLockScreenLandscapeEnabled) {
+            ConstraintSet cs = mContainerMotionLayout.getConstraintSet(R.id.single_constraints);
+            cs.setGuidelinePercent(R.id.pattern_top_guideline,
+                    mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+            cs.applyTo(mContainerMotionLayout);
+        } else {
+            ConstraintSet cs = new ConstraintSet();
+            cs.clone(mContainerConstraintLayout);
+            cs.setGuidelinePercent(R.id.pattern_top_guideline,
+                    mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+            cs.applyTo(mContainerConstraintLayout);
+        }
+    }
+
+    /**
+     * Updates the keyguard view's constraints (single or split constraints).
+     * Split constraints are only used for small landscape screens.
+     * Only called when flag LANDSCAPE_ENABLE_LOCKSCREEN is enabled.
+     */
+    @Override
+    protected void updateConstraints(boolean useSplitBouncer) {
+        mAlreadyUsingSplitBouncer = useSplitBouncer;
+        if (useSplitBouncer) {
+            mContainerMotionLayout.jumpToState(R.id.split_constraints);
+            mContainerMotionLayout.setMaxWidth(Integer.MAX_VALUE);
+        } else {
+            mContainerMotionLayout.jumpToState(R.id.single_constraints);
+            mContainerMotionLayout.setMaxWidth(getResources()
+                    .getDimensionPixelSize(R.dimen.biometric_auth_pattern_view_max_size));
+        }
     }
 
     @Override
@@ -130,7 +190,6 @@
         mLockPatternView = findViewById(R.id.lockPatternView);
 
         mEcaView = findViewById(R.id.keyguard_selector_fade_container);
-        mContainer = findViewById(R.id.pattern_container);
     }
 
     @Override
@@ -209,7 +268,7 @@
                 getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR));
 
         DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
-                        ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
+                ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
         disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
                 () -> {
                     enableClipping(true);
@@ -220,7 +279,7 @@
         if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
             mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
                     (long) (200 * durationMultiplier),
-                    - mDisappearAnimationUtils.getStartTranslation() * 3,
+                    -mDisappearAnimationUtils.getStartTranslation() * 3,
                     false /* appearing */,
                     mDisappearAnimationUtils.getInterpolator(),
                     null /* finishRunnable */);
@@ -229,9 +288,16 @@
     }
 
     private void enableClipping(boolean enable) {
-        setClipChildren(enable);
-        mContainer.setClipToPadding(enable);
-        mContainer.setClipChildren(enable);
+        if (mContainerConstraintLayout != null) {
+            setClipChildren(enable);
+            mContainerConstraintLayout.setClipToPadding(enable);
+            mContainerConstraintLayout.setClipChildren(enable);
+        }
+        if (mContainerMotionLayout != null) {
+            setClipChildren(enable);
+            mContainerMotionLayout.setClipToPadding(enable);
+            mContainerMotionLayout.setClipChildren(enable);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index a30b447..98312b1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
 import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 
 import android.content.res.ColorStateList;
 import android.os.AsyncTask;
@@ -205,6 +206,8 @@
         mLatencyTracker = latencyTracker;
         mFalsingCollector = falsingCollector;
         mEmergencyButtonController = emergencyButtonController;
+        view.setIsLockScreenLandscapeEnabled(
+                featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
         mLockPatternView = mView.findViewById(R.id.lockPatternView);
         mPostureController = postureController;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 574a059..0af803f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+
 import android.view.View;
 
 import com.android.internal.util.LatencyTracker;
@@ -61,6 +63,7 @@
         mPostureController = postureController;
         mLockPatternUtils = lockPatternUtils;
         mFeatureFlags = featureFlags;
+        view.setIsLockScreenLandscapeEnabled(mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
         mBackspaceKey = view.findViewById(R.id.delete_button);
         mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 4cc90c2..f18504c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 
 import android.util.Log;
@@ -154,9 +155,9 @@
     private int getLayoutIdFor(SecurityMode securityMode) {
         // TODO (b/297863911, b/297864907) - implement motion layout for other bouncers
         switch (securityMode) {
-            case Pattern: return R.layout.keyguard_pattern_view;
+            case Pattern: return R.layout.keyguard_pattern_motion_layout;
             case PIN: return R.layout.keyguard_pin_motion_layout;
-            case Password: return R.layout.keyguard_password_view;
+            case Password: return R.layout.keyguard_password_motion_layout;
             case SimPin: return R.layout.keyguard_sim_pin_view;
             case SimPuk: return R.layout.keyguard_sim_puk_view;
             default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e4bbd3a..abab5e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -35,6 +35,7 @@
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
 import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
 import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
+
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -74,6 +75,7 @@
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
@@ -160,6 +162,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
@@ -202,7 +205,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
 
@@ -342,15 +344,16 @@
                 return;
             }
 
-            if (mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
+            if (mWakefulness.getWakefulness() == WAKEFULNESS_AWAKE
+                    && mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
                     == Display.STATE_OFF) {
-                mAllowedDisplayStateForFaceAuth = false;
+                mAllowedDisplayStateWhileAwakeForFaceAuth = false;
                 updateFaceListeningState(
                         BIOMETRIC_ACTION_STOP,
                         FACE_AUTH_DISPLAY_OFF
                 );
             } else {
-                mAllowedDisplayStateForFaceAuth = true;
+                mAllowedDisplayStateWhileAwakeForFaceAuth = true;
             }
         }
     };
@@ -374,7 +377,7 @@
     private boolean mOccludingAppRequestingFp;
     private boolean mOccludingAppRequestingFace;
     private boolean mSecureCameraLaunched;
-    private boolean mAllowedDisplayStateForFaceAuth = true;
+    private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true;
     @VisibleForTesting
     protected boolean mTelephonyCapable;
     private boolean mAllowFingerprintOnCurrentOccludingActivity;
@@ -422,6 +425,7 @@
     private KeyguardFaceAuthInteractor mFaceAuthInteractor;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final IActivityTaskManager mActivityTaskManager;
+    private final WakefulnessLifecycle mWakefulness;
     private final DisplayTracker mDisplayTracker;
     private final LockPatternUtils mLockPatternUtils;
     @VisibleForTesting
@@ -2207,7 +2211,7 @@
         Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
         Assert.isMainThread();
 
-        mAllowedDisplayStateForFaceAuth = true;
+        mAllowedDisplayStateWhileAwakeForFaceAuth = true;
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         if (mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason)) {
             FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
@@ -2363,7 +2367,8 @@
             Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
             TaskStackChangeListeners taskStackChangeListeners,
             IActivityTaskManager activityTaskManagerService,
-            DisplayTracker displayTracker) {
+            DisplayTracker displayTracker,
+            WakefulnessLifecycle wakefulness) {
         mContext = context;
         mSubscriptionManager = subscriptionManager;
         mUserTracker = userTracker;
@@ -2410,6 +2415,7 @@
                 .collect(Collectors.toSet());
         mTaskStackChangeListeners = taskStackChangeListeners;
         mActivityTaskManager = activityTaskManagerService;
+        mWakefulness = wakefulness;
         mDisplayTracker = displayTracker;
         mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
 
@@ -2439,7 +2445,7 @@
                         handleDevicePolicyManagerStateChanged(msg.arg1);
                         break;
                     case MSG_USER_SWITCHING:
-                        handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj);
+                        handleUserSwitching(msg.arg1, (Runnable) msg.obj);
                         break;
                     case MSG_USER_SWITCH_COMPLETE:
                         handleUserSwitchComplete(msg.arg1);
@@ -2740,10 +2746,12 @@
     }
 
     private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
+
         @Override
-        public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) {
+        public void onUserChanging(int newUser, @NonNull Context userContext,
+                @NonNull Runnable resultCallback) {
             mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
-                    newUser, 0, latch));
+                    newUser, 0, resultCallback));
         }
 
         @Override
@@ -3212,7 +3220,7 @@
                 && faceAndFpNotAuthenticated
                 && !mGoingToSleep
                 && isPostureAllowedForFaceAuth
-                && mAllowedDisplayStateForFaceAuth;
+                && mAllowedDisplayStateWhileAwakeForFaceAuth;
 
         // Aggregate relevant fields for debug logging.
         logListenerModelData(
@@ -3220,7 +3228,7 @@
                     System.currentTimeMillis(),
                     user,
                     shouldListen,
-                    mAllowedDisplayStateForFaceAuth,
+                    mAllowedDisplayStateWhileAwakeForFaceAuth,
                     mAlternateBouncerShowing,
                     mAuthInterruptActive,
                     biometricEnabledForUser,
@@ -3574,7 +3582,7 @@
      * Handle {@link #MSG_USER_SWITCHING}
      */
     @VisibleForTesting
-    void handleUserSwitching(int userId, CountDownLatch latch) {
+    void handleUserSwitching(int userId, Runnable resultCallback) {
         mLogger.logUserSwitching(userId, "from UserTracker");
         Assert.isMainThread();
         clearBiometricRecognized();
@@ -3588,7 +3596,7 @@
                 cb.onUserSwitching(userId);
             }
         }
-        latch.countDown();
+        resultCallback.run();
     }
 
     /**
@@ -4201,7 +4209,7 @@
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
                 final boolean previousState = mAllowFingerprintOnCurrentOccludingActivity;
                 mAllowFingerprintOnCurrentOccludingActivity =
-                        standardTask.topActivity != null
+                        standardTask != null && standardTask.topActivity != null
                                 && !TextUtils.isEmpty(standardTask.topActivity.getPackageName())
                                 && mAllowFingerprintOnOccludingActivitiesFromPackage.contains(
                                         standardTask.topActivity.getPackageName())
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 3990b10..c64ae01 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
 import android.util.Property;
 import android.view.View;
@@ -124,7 +125,16 @@
                     true /* animate */);
             log("keyguardFadingAway transition w/ Y Aniamtion");
         } else if (statusBarState == KEYGUARD) {
-            if (keyguardFadingAway) {
+            // Sometimes, device will be unlocked and then locked very quickly.
+            // keyguardFadingAway hasn't been set to false cause unlock animation hasn't finished
+            // So we should not animate keyguard fading away in this case (when oldState is SHADE)
+            if (oldStatusBarState != SHADE) {
+                log("statusBarState == KEYGUARD && oldStatusBarState != SHADE");
+            } else {
+                log("statusBarState == KEYGUARD && oldStatusBarState == SHADE");
+            }
+
+            if (keyguardFadingAway && oldStatusBarState != SHADE) {
                 mKeyguardViewVisibilityAnimating = true;
                 AnimationProperties animProps = new AnimationProperties()
                         .setDelay(0)
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 1d37809..c522881 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -134,8 +134,6 @@
         mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
                 mLockIconPadding);
 
-        // mSensorProps coordinates assume portrait mode which is OK b/c the keyguard is always in
-        // portrait.
         mSensorRect.set(mLockIconCenter.x - mRadius,
                 mLockIconCenter.y - mRadius,
                 mLockIconCenter.x + mRadius,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 4649091..214b122 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
+
 import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
@@ -44,6 +45,7 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -422,7 +424,13 @@
     private void updateConfiguration() {
         WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
+        WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
         mWidthPixels = bounds.right;
+        if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
+            // Assumed to be initially neglected as there are no left or right insets in portrait
+            // However, on landscape, these insets need to included when calculating the midpoint
+            mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
+        }
         mHeightPixels = bounds.bottom;
         mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
         mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
index 12108b0..b15aaaf 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
@@ -25,6 +25,9 @@
 import android.util.Log
 import androidx.core.app.AppComponentFactory
 import com.android.systemui.dagger.ContextComponentHelper
+import com.android.systemui.dagger.SysUIComponent
+import com.android.tools.r8.keepanno.annotations.KeepTarget
+import com.android.tools.r8.keepanno.annotations.UsesReflection
 import java.lang.reflect.InvocationTargetException
 import java.util.concurrent.ExecutionException
 import javax.inject.Inject
@@ -88,6 +91,7 @@
         return app
     }
 
+    @UsesReflection(KeepTarget(extendsClassConstant = SysUIComponent::class, methodName = "inject"))
     override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
         val contentProvider = super.instantiateProviderCompat(cl, className)
         if (contentProvider is ContextInitializer) {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index c9801d7..9fd0602 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -54,6 +54,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -84,6 +85,7 @@
     private final SystemClock mClock;
 
     private H mBGHandler;
+    private final Executor mBgExecutor;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
     private boolean mListening;
@@ -153,6 +155,7 @@
     public AppOpsControllerImpl(
             Context context,
             @Background Looper bgLooper,
+            @Background Executor bgExecutor,
             DumpManager dumpManager,
             AudioManager audioManager,
             IndividualSensorPrivacyController sensorPrivacyController,
@@ -162,6 +165,7 @@
         mDispatcher = dispatcher;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mBGHandler = new H(bgLooper);
+        mBgExecutor = bgExecutor;
         final int numOps = OPS.length;
         for (int i = 0; i < numOps; i++) {
             mCallbacksByCode.put(OPS[i], new ArraySet<>());
@@ -184,41 +188,43 @@
     @VisibleForTesting
     protected void setListening(boolean listening) {
         mListening = listening;
-        if (listening) {
-            // System UI could be restarted while ops are active, so fetch the currently active ops
-            // once System UI starts listening again.
-            fetchCurrentActiveOps();
+        // Move IPCs to the background.
+        mBgExecutor.execute(() -> {
+            if (listening) {
+                // System UI could be restarted while ops are active, so fetch the currently active
+                // ops once System UI starts listening again -- see b/294104969.
+                fetchCurrentActiveOps();
 
-            mAppOps.startWatchingActive(OPS, this);
-            mAppOps.startWatchingNoted(OPS, this);
-            mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
-            mSensorPrivacyController.addCallback(this);
+                mAppOps.startWatchingActive(OPS, this);
+                mAppOps.startWatchingNoted(OPS, this);
+                mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
+                mSensorPrivacyController.addCallback(this);
 
-            mMicMuted = mAudioManager.isMicrophoneMute()
-                    || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
-            mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
+                mMicMuted = mAudioManager.isMicrophoneMute()
+                        || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+                mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
 
-            mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
-                    mAudioManager.getActiveRecordingConfigurations()));
-            mDispatcher.registerReceiverWithHandler(this,
-                    new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
+                mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
+                        mAudioManager.getActiveRecordingConfigurations()));
+                mDispatcher.registerReceiverWithHandler(this,
+                        new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
+            } else {
+                mAppOps.stopWatchingActive(this);
+                mAppOps.stopWatchingNoted(this);
+                mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+                mSensorPrivacyController.removeCallback(this);
 
-        } else {
-            mAppOps.stopWatchingActive(this);
-            mAppOps.stopWatchingNoted(this);
-            mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
-            mSensorPrivacyController.removeCallback(this);
-
-            mBGHandler.removeCallbacksAndMessages(null); // null removes all
-            mDispatcher.unregisterReceiver(this);
-            synchronized (mActiveItems) {
-                mActiveItems.clear();
-                mRecordingsByUid.clear();
+                mBGHandler.removeCallbacksAndMessages(null); // null removes all
+                mDispatcher.unregisterReceiver(this);
+                synchronized (mActiveItems) {
+                    mActiveItems.clear();
+                    mRecordingsByUid.clear();
+                }
+                synchronized (mNotedItems) {
+                    mNotedItems.clear();
+                }
             }
-            synchronized (mNotedItems) {
-                mNotedItems.clear();
-            }
-        }
+        });
     }
 
     private void fetchCurrentActiveOps() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index f26404ca..4416b19 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -309,8 +309,14 @@
         }
 
         int invocationType = args.getInt(INVOCATION_TYPE_KEY);
-        return mAssistOverrideInvocationTypes != null && Arrays.stream(
-                mAssistOverrideInvocationTypes).anyMatch(override -> override == invocationType);
+        return shouldOverrideAssist(invocationType);
+    }
+
+    /** @return true if the invocation type should be handled by OverviewProxy instead of SysUI. */
+    public boolean shouldOverrideAssist(int invocationType) {
+        return mAssistOverrideInvocationTypes != null
+                && Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
+                        override -> override == invocationType);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 97b0617..054bd08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -25,7 +25,7 @@
             shadeExpansionCollectorJob =
                 scope.launch {
                     // wait for it to emit true once
-                    shadeInteractorLazy.get().anyExpanding.first { it }
+                    shadeInteractorLazy.get().isAnyExpanding.first { it }
                     onShadeInteraction.run()
                 }
             shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9ffb770..0264356 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -23,6 +23,7 @@
 import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
 import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
 import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
@@ -104,6 +105,8 @@
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.SystemClock;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -114,8 +117,6 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import kotlin.Unit;
-
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 /**
@@ -255,11 +256,12 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+        final int touchConfigId = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_selected_udfps_touch_detection);
         pw.println("mSensorProps=(" + mSensorProps + ")");
         pw.println("Using new touch detection framework: " + mFeatureFlags.isEnabled(
                 Flags.UDFPS_NEW_TOUCH_DETECTION));
-        pw.println("Using ellipse touch detection: " + mFeatureFlags.isEnabled(
-                Flags.UDFPS_ELLIPSE_DETECTION));
+        pw.println("touchConfigId: " + touchConfigId);
     }
 
     public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index f43285f..f2d4f89 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.biometrics.dagger
 
 import com.android.systemui.biometrics.UdfpsUtils
+import android.content.res.Resources
+import com.android.internal.R
+import com.android.systemui.biometrics.EllipseOverlapDetectorParams
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.biometrics.data.repository.FacePropertyRepositoryImpl
 import com.android.systemui.biometrics.data.repository.FaceSettingsRepository
@@ -25,8 +28,8 @@
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
 import com.android.systemui.biometrics.data.repository.PromptRepository
 import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
+import com.android.systemui.biometrics.data.repository.DisplayStateRepository
+import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
 import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
 import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -37,6 +40,9 @@
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
 import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl
+import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
+import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
+import com.android.systemui.biometrics.udfps.OverlapDetector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.util.concurrency.ThreadFactory
 import dagger.Binds
@@ -63,16 +69,19 @@
 
     @Binds
     @SysUISingleton
-    fun fingerprintRepository(impl: FingerprintPropertyRepositoryImpl):
-            FingerprintPropertyRepository
-    @Binds
-    @SysUISingleton
-    fun rearDisplayStateRepository(impl: RearDisplayStateRepositoryImpl): RearDisplayStateRepository
+    fun fingerprintRepository(
+        impl: FingerprintPropertyRepositoryImpl
+    ): FingerprintPropertyRepository
 
     @Binds
     @SysUISingleton
-    fun providesPromptSelectorInteractor(impl: PromptSelectorInteractorImpl):
-            PromptSelectorInteractor
+    fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository
+
+    @Binds
+    @SysUISingleton
+    fun providesPromptSelectorInteractor(
+        impl: PromptSelectorInteractorImpl
+    ): PromptSelectorInteractor
 
     @Binds
     @SysUISingleton
@@ -88,8 +97,9 @@
 
     @Binds
     @SysUISingleton
-    fun providesSideFpsOverlayInteractor(impl: SideFpsOverlayInteractorImpl):
-            SideFpsOverlayInteractor
+    fun providesSideFpsOverlayInteractor(
+        impl: SideFpsOverlayInteractorImpl
+    ): SideFpsOverlayInteractor
 
     companion object {
         /** Background [Executor] for HAL related operations. */
@@ -102,6 +112,30 @@
 
         @Provides
         fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils()
+
+        @Provides
+        @SysUISingleton
+        fun providesOverlapDetector(): OverlapDetector {
+            val selectedOption =
+                Resources.getSystem().getInteger(R.integer.config_selected_udfps_touch_detection)
+            val values =
+                Resources.getSystem()
+                    .getStringArray(R.array.config_udfps_touch_detection_options)[selectedOption]
+                    .split(",")
+                    .map { it.toFloat() }
+
+            return if (values[0] == 1f) {
+                EllipseOverlapDetector(
+                    EllipseOverlapDetectorParams(
+                        minOverlap = values[3],
+                        targetSize = values[2],
+                        stepSize = values[4].toInt()
+                    )
+                )
+            } else {
+                BoundingBoxOverlapDetector(values[2])
+            }
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
deleted file mode 100644
index f7f9103..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 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.dagger
-
-import android.content.res.Resources
-import com.android.internal.R
-import com.android.systemui.biometrics.EllipseOverlapDetectorParams
-import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
-import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
-import com.android.systemui.biometrics.udfps.OverlapDetector
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import dagger.Module
-import dagger.Provides
-
-/** Dagger module for all things UDFPS. TODO(b/260558624): Move to BiometricsModule. */
-@Module
-interface UdfpsModule {
-    companion object {
-
-        @Provides
-        @SysUISingleton
-        fun providesOverlapDetector(featureFlags: FeatureFlags): OverlapDetector {
-            if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
-                val selectedOption =
-                    Resources.getSystem()
-                        .getInteger(R.integer.config_selected_udfps_touch_detection)
-                val values =
-                    Resources.getSystem()
-                        .getStringArray(R.array.config_udfps_touch_detection_options)[
-                            selectedOption]
-                        .split(",")
-                        .map { it.toFloat() }
-
-                return if (values[0] == 1f) {
-                    EllipseOverlapDetector(
-                        EllipseOverlapDetectorParams(
-                            minOverlap = values[3],
-                            targetSize = values[2],
-                            stepSize = values[4].toInt()
-                        )
-                    )
-                } else {
-                    BoundingBoxOverlapDetector(values[2])
-                }
-            } else {
-                return BoundingBoxOverlapDetector(1f)
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
new file mode 100644
index 0000000..7a9efcf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.data.repository
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+import android.os.Handler
+import android.view.DisplayInfo
+import com.android.internal.util.ArrayUtils
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.toDisplayRotation
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Repository for the current state of the display */
+interface DisplayStateRepository {
+    /** Provides the current rear display state. */
+    val isInRearDisplayMode: StateFlow<Boolean>
+
+    /** Provides the current display rotation */
+    val currentRotation: StateFlow<DisplayRotation>
+}
+
+@SysUISingleton
+class DisplayStateRepositoryImpl
+@Inject
+constructor(
+    @Application applicationScope: CoroutineScope,
+    @Application val context: Context,
+    deviceStateManager: DeviceStateManager,
+    displayManager: DisplayManager,
+    @Main handler: Handler,
+    @Main mainExecutor: Executor
+) : DisplayStateRepository {
+    override val isInRearDisplayMode: StateFlow<Boolean> =
+        conflatedCallbackFlow {
+                val sendRearDisplayStateUpdate = { state: Boolean ->
+                    trySendWithFailureLogging(
+                        state,
+                        TAG,
+                        "Error sending rear display state update to $state"
+                    )
+                }
+
+                val callback =
+                    DeviceStateManager.DeviceStateCallback { state ->
+                        val isInRearDisplayMode =
+                            ArrayUtils.contains(
+                                context.resources.getIntArray(
+                                    com.android.internal.R.array.config_rearDisplayDeviceStates
+                                ),
+                                state
+                            )
+                        sendRearDisplayStateUpdate(isInRearDisplayMode)
+                    }
+
+                sendRearDisplayStateUpdate(false)
+                deviceStateManager.registerCallback(mainExecutor, callback)
+                awaitClose { deviceStateManager.unregisterCallback(callback) }
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = false,
+            )
+
+    private fun getDisplayRotation(): DisplayRotation {
+        val cachedDisplayInfo = DisplayInfo()
+        context.display?.getDisplayInfo(cachedDisplayInfo)
+        return cachedDisplayInfo.rotation.toDisplayRotation()
+    }
+
+    override val currentRotation: StateFlow<DisplayRotation> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : DisplayListener {
+                        override fun onDisplayRemoved(displayId: Int) {}
+
+                        override fun onDisplayAdded(displayId: Int) {}
+
+                        override fun onDisplayChanged(displayId: Int) {
+                            val rotation = getDisplayRotation()
+                            trySendWithFailureLogging(
+                                rotation,
+                                TAG,
+                                "Error sending display rotation to $rotation"
+                            )
+                        }
+                    }
+                displayManager.registerDisplayListener(
+                    callback,
+                    handler,
+                    EVENT_FLAG_DISPLAY_CHANGED
+                )
+                awaitClose { displayManager.unregisterDisplayListener(callback) }
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = getDisplayRotation(),
+            )
+
+    companion object {
+        const val TAG = "DisplayStateRepositoryImpl"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
deleted file mode 100644
index d17d961..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.data.repository
-
-import android.content.Context
-import android.hardware.devicestate.DeviceStateManager
-import com.android.internal.util.ArrayUtils
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.stateIn
-
-/** Provide current rear display state. */
-interface RearDisplayStateRepository {
-    /** Provides the current rear display state. */
-    val isInRearDisplayMode: StateFlow<Boolean>
-}
-
-@SysUISingleton
-class RearDisplayStateRepositoryImpl
-@Inject
-constructor(
-    @Application applicationScope: CoroutineScope,
-    @Application context: Context,
-    deviceStateManager: DeviceStateManager,
-    @Main mainExecutor: Executor
-) : RearDisplayStateRepository {
-    override val isInRearDisplayMode: StateFlow<Boolean> =
-        conflatedCallbackFlow {
-                val sendRearDisplayStateUpdate = { state: Boolean ->
-                    trySendWithFailureLogging(
-                        state,
-                        TAG,
-                        "Error sending rear display state update to $state"
-                    )
-                }
-
-                val callback =
-                    DeviceStateManager.DeviceStateCallback { state ->
-                        val isInRearDisplayMode =
-                            ArrayUtils.contains(
-                                context.resources.getIntArray(
-                                    com.android.internal.R.array.config_rearDisplayDeviceStates
-                                ),
-                                state
-                            )
-                        sendRearDisplayStateUpdate(isInRearDisplayMode)
-                    }
-
-                sendRearDisplayStateUpdate(false)
-                deviceStateManager.registerCallback(mainExecutor, callback)
-                awaitClose { deviceStateManager.unregisterCallback(callback) }
-            }
-            .stateIn(
-                applicationScope,
-                started = SharingStarted.Eagerly,
-                initialValue = false,
-            )
-
-    companion object {
-        const val TAG = "RearDisplayStateRepositoryImpl"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
index 2e734a3..f36a3ec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -19,7 +19,8 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.view.Display
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.DisplayStateRepository
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Application
@@ -50,6 +51,9 @@
     /** Whether the device is currently folded. */
     val isFolded: Flow<Boolean>
 
+    /** Current rotation of the display */
+    val currentRotation: StateFlow<DisplayRotation>
+
     /** Called on configuration changes, used to keep the display state in sync */
     fun onConfigurationChanged(newConfig: Configuration)
 }
@@ -61,7 +65,7 @@
     @Application applicationScope: CoroutineScope,
     @Application context: Context,
     @Main mainExecutor: Executor,
-    rearDisplayStateRepository: RearDisplayStateRepository,
+    displayStateRepository: DisplayStateRepository,
     displayRepository: DisplayRepository,
 ) : DisplayStateInteractor {
     private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
@@ -98,7 +102,10 @@
             )
 
     override val isInRearDisplayMode: StateFlow<Boolean> =
-        rearDisplayStateRepository.isInRearDisplayMode
+        displayStateRepository.isInRearDisplayMode
+
+    override val currentRotation: StateFlow<DisplayRotation> =
+        displayStateRepository.currentRotation
 
     override fun onConfigurationChanged(newConfig: Configuration) {
         screenSizeFoldProvider.onConfigurationChange(newConfig)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
new file mode 100644
index 0000000..10a3e91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.biometrics.shared.model
+
+import android.view.Surface
+
+/** Shadows [Surface.Rotation] for kotlin use within SysUI. */
+enum class DisplayRotation {
+    ROTATION_0,
+    ROTATION_90,
+    ROTATION_180,
+    ROTATION_270,
+}
+
+/** Converts [Surface.Rotation] to corresponding [DisplayRotation] */
+fun Int.toDisplayRotation(): DisplayRotation =
+    when (this) {
+        Surface.ROTATION_0 -> DisplayRotation.ROTATION_0
+        Surface.ROTATION_90 -> DisplayRotation.ROTATION_90
+        Surface.ROTATION_180 -> DisplayRotation.ROTATION_180
+        Surface.ROTATION_270 -> DisplayRotation.ROTATION_270
+        else -> throw IllegalArgumentException("Invalid DisplayRotation value: $this")
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index d616dcc..02847c2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -252,6 +252,18 @@
                     }
                 }
 
+                // set padding
+                launch {
+                    viewModel.promptPadding.collect { promptPadding ->
+                        view.setPadding(
+                            promptPadding.left,
+                            promptPadding.top,
+                            promptPadding.right,
+                            promptPadding.bottom
+                        )
+                    }
+                }
+
                 // configure & hide/disable buttons
                 launch {
                     viewModel.credentialKind
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 6269700..267afae 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
@@ -15,16 +15,21 @@
  */
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.content.Context
+import android.graphics.Rect
 import android.hardware.biometrics.BiometricPrompt
 import android.util.Log
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
+import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.biometrics.ui.binder.Spaghetti
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.statusbar.VibratorHelper
@@ -49,6 +54,7 @@
     private val displayStateInteractor: DisplayStateInteractor,
     private val promptSelectorInteractor: PromptSelectorInteractor,
     private val vibrator: VibratorHelper,
+    @Application context: Context,
     private val featureFlags: FeatureFlags,
 ) {
     /** Models UI of [BiometricPromptLayout.iconView] */
@@ -135,6 +141,23 @@
             !isOverlayTouched && size.isNotSmall
         }
 
+    /** Padding for prompt UI elements */
+    val promptPadding: Flow<Rect> =
+        combine(size, displayStateInteractor.currentRotation) { size, rotation ->
+            if (size != PromptSize.LARGE) {
+                val navBarInsets = Utils.getNavbarInsets(context)
+                if (rotation == DisplayRotation.ROTATION_90) {
+                    Rect(0, 0, navBarInsets.right, 0)
+                } else if (rotation == DisplayRotation.ROTATION_270) {
+                    Rect(navBarInsets.left, 0, 0, 0)
+                } else {
+                    Rect(0, 0, 0, navBarInsets.bottom)
+                }
+            } else {
+                Rect(0, 0, 0, 0)
+            }
+        }
+
     /** Title for the prompt. */
     val title: Flow<String> =
         promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 364b5e7..1985c37 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -129,7 +129,12 @@
                     buildList {
                         var dot = previousDot
                         while (dot != hitDot) {
-                            add(dot)
+                            // Move along the direction of the line connecting the previously
+                            // selected dot and current hit dot, and see if they were skipped over
+                            // but fall on that line.
+                            if (dot.isOnLineSegment(previousDot, hitDot)) {
+                                add(dot)
+                            }
                             dot =
                                 PatternDotViewModel(
                                     x =
@@ -208,6 +213,34 @@
     }
 }
 
+/**
+ * Determines whether [this] dot is present on the line segment connecting [first] and [second]
+ * dots.
+ */
+private fun PatternDotViewModel.isOnLineSegment(
+    first: PatternDotViewModel,
+    second: PatternDotViewModel
+): Boolean {
+    val anotherPoint = this
+    // No need to consider any points outside the bounds of two end points
+    val isWithinBounds =
+        anotherPoint.x.isBetween(first.x, second.x) && anotherPoint.y.isBetween(first.y, second.y)
+    if (!isWithinBounds) {
+        return false
+    }
+
+    // Uses the 2 point line equation: (y-y1)/(x-x1) = (y2-y1)/(x2-x1)
+    // which can be rewritten as:      (y-y1)*(x2-x1) = (x-x1)*(y2-y1)
+    // This is true for any point on the line passing through these two points
+    return (anotherPoint.y - first.y) * (second.x - first.x) ==
+        (anotherPoint.x - first.x) * (second.y - first.y)
+}
+
+/** Is [this] Int between [a] and [b] */
+private fun Int.isBetween(a: Int, b: Int): Boolean {
+    return (this in a..b) || (this in b..a)
+}
+
 data class PatternDotViewModel(
     val x: Int,
     val y: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
new file mode 100644
index 0000000..ea3ddac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.communal.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** Encapsulates the state of communal mode. */
+interface CommunalRepository {
+    /** Whether communal features are enabled. */
+    val isCommunalEnabled: Boolean
+}
+
+@SysUISingleton
+class CommunalRepositoryImpl
+@Inject
+constructor(
+    featureFlags: FeatureFlagsClassic,
+) : CommunalRepository {
+    override val isCommunalEnabled = featureFlags.isEnabled(Flags.COMMUNAL_HUB)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
new file mode 100644
index 0000000..9d95b9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
@@ -0,0 +1,9 @@
+package com.android.systemui.communal.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalRepositoryModule {
+    @Binds fun communalRepository(impl: CommunalRepositoryImpl): CommunalRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 6dc305e..9fb8da3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.domain.interactor
 
+import com.android.systemui.communal.data.repository.CommunalRepository
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.shared.CommunalAppWidgetInfo
 import com.android.systemui.dagger.SysUISingleton
@@ -27,8 +28,12 @@
 class CommunalInteractor
 @Inject
 constructor(
+    communalRepository: CommunalRepository,
     widgetRepository: CommunalWidgetRepository,
 ) {
+    /** Whether communal features are enabled. */
+    val isCommunalEnabled: Boolean = communalRepository.isCommunalEnabled
+
     /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
     val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 7b58b1f..9e5fd55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -31,6 +31,7 @@
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.media.dagger.MediaModule;
+import com.android.systemui.navigationbar.NavigationBarControllerModule;
 import com.android.systemui.navigationbar.gestural.GestureModule;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -101,6 +102,7 @@
         GestureModule.class,
         MediaModule.class,
         MultiUserUtilsModule.class,
+        NavigationBarControllerModule.class,
         PowerModule.class,
         QSModule.class,
         ReferenceScreenshotModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index f12b919..d89332d 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.power.PowerUI
 import com.android.systemui.reardisplay.RearDisplayDialogController
 import com.android.systemui.recents.Recents
+import com.android.systemui.recents.ScreenPinningRequest
 import com.android.systemui.settings.dagger.MultiUserUtilsModule
 import com.android.systemui.shortcut.ShortcutKeyDispatcher
 import com.android.systemui.statusbar.ImmersiveModeConfirmation
@@ -366,4 +367,9 @@
     @IntoMap
     @ClassKey(KeyguardDismissBinder::class)
     abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(ScreenPinningRequest::class)
+    abstract fun bindScreenPinningRequest(impl: ScreenPinningRequest): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7ee0ff4..58ba3c9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -37,7 +37,6 @@
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
 import com.android.systemui.biometrics.dagger.BiometricsModule;
-import com.android.systemui.biometrics.dagger.UdfpsModule;
 import com.android.systemui.bouncer.ui.BouncerViewModule;
 import com.android.systemui.classifier.FalsingModule;
 import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
@@ -90,6 +89,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.connectivity.ConnectivityModule;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -154,72 +154,72 @@
  * may not appreciate that.
  */
 @Module(includes = {
-            AccessibilityModule.class,
-            AccessibilityRepositoryModule.class,
-            AConfigModule.class,
-            AppOpsModule.class,
-            AssistModule.class,
-            AuthenticationModule.class,
-            BiometricsModule.class,
-            BouncerViewModule.class,
-            ClipboardOverlayModule.class,
-            ClockRegistryModule.class,
-            CommonRepositoryModule.class,
-            DisplayModule.class,
-            ConnectivityModule.class,
-            CoroutinesModule.class,
-            DreamModule.class,
-            ControlsModule.class,
-            DemoModeModule.class,
-            DisableFlagsModule.class,
-            FalsingModule.class,
-            FlagsModule.class,
-            SystemPropertiesFlagsModule.class,
-            FooterActionsModule.class,
-            KeyboardModule.class,
-            LetterboxModule.class,
-            KeyguardBlueprintModule.class,
-            LogModule.class,
-            MediaProjectionModule.class,
-            MediaProjectionTaskSwitcherModule.class,
-            MotionToolModule.class,
-            NotificationIconAreaControllerModule.class,
-            PeopleHubModule.class,
-            PeopleModule.class,
-            PluginModule.class,
-            PolicyModule.class,
-            PrivacyModule.class,
-            QRCodeScannerModule.class,
-            QSFragmentStartableModule.class,
-            RetailModeModule.class,
-            ScreenshotModule.class,
-            SensorModule.class,
-            SecurityRepositoryModule.class,
-            ScreenRecordModule.class,
-            SettingsUtilModule.class,
-            SmartRepliesInflationModule.class,
-            SmartspaceModule.class,
-            StatusBarPipelineModule.class,
-            StatusBarPolicyModule.class,
-            StatusBarWindowModule.class,
-            SysUIConcurrencyModule.class,
-            SysUIUnfoldModule.class,
-            TelephonyRepositoryModule.class,
-            TemporaryDisplayModule.class,
-            TunerModule.class,
-            UdfpsModule.class,
-            UserModule.class,
-            UtilModule.class,
-            NoteTaskModule.class,
-            WalletModule.class
+        AccessibilityModule.class,
+        AccessibilityRepositoryModule.class,
+        AConfigModule.class,
+        AppOpsModule.class,
+        AssistModule.class,
+        AuthenticationModule.class,
+        BiometricsModule.class,
+        BouncerViewModule.class,
+        ClipboardOverlayModule.class,
+        ClockRegistryModule.class,
+        CommonRepositoryModule.class,
+        ConnectivityModule.class,
+        ControlsModule.class,
+        CoroutinesModule.class,
+        DemoModeModule.class,
+        DisableFlagsModule.class,
+        DisplayModule.class,
+        DreamModule.class,
+        FalsingModule.class,
+        FlagsModule.class,
+        FooterActionsModule.class,
+        KeyboardModule.class,
+        KeyguardBlueprintModule.class,
+        LetterboxModule.class,
+        LogModule.class,
+        MediaProjectionModule.class,
+        MediaProjectionTaskSwitcherModule.class,
+        MotionToolModule.class,
+        NotificationIconAreaControllerModule.class,
+        PeopleHubModule.class,
+        PeopleModule.class,
+        PluginModule.class,
+        PolicyModule.class,
+        PrivacyModule.class,
+        QRCodeScannerModule.class,
+        QSFragmentStartableModule.class,
+        RetailModeModule.class,
+        ScreenshotModule.class,
+        SensorModule.class,
+        SecurityRepositoryModule.class,
+        ScreenRecordModule.class,
+        SettingsUtilModule.class,
+        SmartRepliesInflationModule.class,
+        SmartspaceModule.class,
+        StatusBarModule.class,
+        StatusBarPipelineModule.class,
+        StatusBarPolicyModule.class,
+        StatusBarWindowModule.class,
+        SystemPropertiesFlagsModule.class,
+        SysUIConcurrencyModule.class,
+        SysUIUnfoldModule.class,
+        TelephonyRepositoryModule.class,
+        TemporaryDisplayModule.class,
+        TunerModule.class,
+        UserModule.class,
+        UtilModule.class,
+        NoteTaskModule.class,
+        WalletModule.class
         },
         subcomponents = {
             ComplicationComponent.class,
-            NavigationBarComponent.class,
-            NotificationRowComponent.class,
             DozeComponent.class,
             ExpandableNotificationRowComponent.class,
             KeyguardBouncerComponent.class,
+            NavigationBarComponent.class,
+            NotificationRowComponent.class,
             NotificationShelfComponent.class,
             WindowRootViewComponent.class,
         })
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 1751358..4f16685 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.display.data.repository
 
 import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
 import android.hardware.display.DisplayManager.DisplayListener
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_ADDED
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
@@ -95,28 +96,36 @@
     @Background backgroundCoroutineDispatcher: CoroutineDispatcher
 ) : DisplayRepository {
     // Displays are enabled only after receiving them in [onDisplayAdded]
-    private val allDisplayEvents: Flow<DisplayEvent> = conflatedCallbackFlow {
-        val callback =
-            object : DisplayListener {
-                override fun onDisplayAdded(displayId: Int) {
-                    trySend(DisplayEvent.Added(displayId))
-                }
+    private val allDisplayEvents: Flow<DisplayEvent> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : DisplayListener {
+                        override fun onDisplayAdded(displayId: Int) {
+                            trySend(DisplayEvent.Added(displayId))
+                        }
 
-                override fun onDisplayRemoved(displayId: Int) {
-                    trySend(DisplayEvent.Removed(displayId))
-                }
+                        override fun onDisplayRemoved(displayId: Int) {
+                            trySend(DisplayEvent.Removed(displayId))
+                        }
 
-                override fun onDisplayChanged(displayId: Int) {
-                    trySend(DisplayEvent.Changed(displayId))
-                }
+                        override fun onDisplayChanged(displayId: Int) {
+                            trySend(DisplayEvent.Changed(displayId))
+                        }
+                    }
+                // Triggers an initial event when subscribed. This is needed to avoid getDisplays to
+                // be called when this class is constructed, but only when someone subscribes to
+                // this flow.
+                trySend(DisplayEvent.Changed(Display.DEFAULT_DISPLAY))
+                displayManager.registerDisplayListener(
+                    callback,
+                    backgroundHandler,
+                    EVENT_FLAG_DISPLAY_ADDED or
+                        EVENT_FLAG_DISPLAY_CHANGED or
+                        EVENT_FLAG_DISPLAY_REMOVED,
+                )
+                awaitClose { displayManager.unregisterDisplayListener(callback) }
             }
-        displayManager.registerDisplayListener(
-            callback,
-            backgroundHandler,
-            EVENT_FLAG_DISPLAY_ADDED or EVENT_FLAG_DISPLAY_CHANGED or EVENT_FLAG_DISPLAY_REMOVED,
-        )
-        awaitClose { displayManager.unregisterDisplayListener(callback) }
-    }
+            .flowOn(backgroundCoroutineDispatcher)
 
     override val displayChangeEvent: Flow<Int> =
         allDisplayEvents.filter { it is DisplayEvent.Changed }.map { it.displayId }
@@ -128,7 +137,9 @@
             .stateIn(
                 applicationScope,
                 started = SharingStarted.WhileSubscribed(),
-                initialValue = getDisplays()
+                // To avoid getting displays on this object construction, they are get after the
+                // first event. allDisplayEvents emits a changed event when we subscribe to it.
+                initialValue = emptySet()
             )
 
     private fun getDisplays(): Set<Display> =
@@ -146,12 +157,23 @@
 
     private val ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
 
+    private fun getInitialConnectedDisplays(): Set<Int> =
+        displayManager
+            .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+            .map { it.displayId }
+            .toSet()
+            .also {
+                if (DEBUG) {
+                    Log.d(TAG, "getInitialConnectedDisplays: $it")
+                }
+            }
+
     /* keeps connected displays until they are disconnected. */
     private val connectedDisplayIds: StateFlow<Set<Int>> =
         conflatedCallbackFlow {
+                val connectedIds = getInitialConnectedDisplays().toMutableSet()
                 val callback =
                     object : DisplayConnectionListener {
-                        private val connectedIds = mutableSetOf<Int>()
                         override fun onDisplayConnected(id: Int) {
                             if (DEBUG) {
                                 Log.d(TAG, "display with id=$id connected.")
@@ -170,6 +192,7 @@
                             trySend(connectedIds.toSet())
                         }
                     }
+                trySend(connectedIds.toSet())
                 displayManager.registerDisplayListener(
                     callback,
                     backgroundHandler,
@@ -183,6 +206,10 @@
             .stateIn(
                 applicationScope,
                 started = SharingStarted.WhileSubscribed(),
+                // The initial value is set to empty, but connected displays are gathered as soon as
+                // the flow starts being collected. This is to ensure the call to get displays (an
+                // IPC) happens in the background instead of when this object
+                // is instantiated.
                 initialValue = emptySet()
             )
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 7e8f682..02575eb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -107,7 +107,7 @@
 
     // TODO(b/268005230): Tracking Bug
     @JvmField
-    val SENSITIVE_REVEAL_ANIM = unreleasedFlag("sensitive_reveal_anim", teamfood = true)
+    val SENSITIVE_REVEAL_ANIM = releasedFlag("sensitive_reveal_anim")
 
     // TODO(b/280783617): Tracking Bug
     @Keep
@@ -115,7 +115,7 @@
     val BUILDER_EXTRAS_OVERRIDE =
         sysPropBooleanFlag(
             "persist.sysui.notification.builder_extras_override",
-            default = false
+            default = true
         )
 
     /** Only notify group expansion listeners when a change happens. */
@@ -182,15 +182,12 @@
 
     /** Flag to control the migration of face auth to modern architecture. */
     // TODO(b/262838215): Tracking bug
-    @JvmField val FACE_AUTH_REFACTOR = unreleasedFlag("face_auth_refactor", teamfood = true)
+    @JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor")
 
     /** Flag to control the revamp of keyguard biometrics progress animation */
     // TODO(b/244313043): Tracking bug
     @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
 
-    // TODO(b/262780002): Tracking Bug
-    @JvmField val REVAMPED_WALLPAPER_UI = releasedFlag("revamped_wallpaper_ui")
-
     // flag for controlling auto pin confirmation and material u shapes in bouncer
     @JvmField
     val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
@@ -294,6 +291,11 @@
     // TODO(b/291767565): Tracking bug.
     @JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag("migrate_keyguard_status_view")
 
+    /** Migrate the status bar view on keyguard from notification panel to keyguard root view. */
+    // TODO(b/299115332): Tracking Bug.
+    @JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
+        unreleasedFlag("migrate_keyguard_status_bar_view")
+
     /** Enables preview loading animation in the wallpaper picker. */
     // TODO(b/274443705): Tracking Bug
     @JvmField
@@ -318,6 +320,11 @@
             R.bool.flag_stop_pulsing_face_scanning_animation,
             "stop_pulsing_face_scanning_animation")
 
+    /** Flag to use a separate view for the alternate bouncer. */
+    // TODO(b/300440924): Tracking bug
+    @JvmField
+    val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
@@ -403,17 +410,7 @@
 
     // TODO(b/290676905): Tracking Bug
     val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
-        unreleasedFlag("new_shade_carrier_group_mobile_icons", teamfood = true)
-
-    // 700 - dialer/calls
-    // TODO(b/254512734): Tracking Bug
-    val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag("ongoing_call_status_bar_chip")
-
-    // TODO(b/254512681): Tracking Bug
-    val ONGOING_CALL_IN_IMMERSIVE = releasedFlag("ongoing_call_in_immersive")
-
-    // TODO(b/254512753): Tracking Bug
-    val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = releasedFlag("ongoing_call_in_immersive_chip_tap")
+        releasedFlag("new_shade_carrier_group_mobile_icons")
 
     // 800 - general visual/theme
     @JvmField val MONET = resourceBooleanFlag(R.bool.flag_monet, "monet")
@@ -662,7 +659,6 @@
     // 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
     // TODO(b/259264861): Tracking Bug
     @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection")
-    @JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag("udfps_ellipse_detection")
 
     // 2300 - stylus
     @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag("track_stylus_ever_used")
@@ -767,14 +763,14 @@
 
     // TODO(b/285174336): Tracking Bug
     @JvmField
-    val USE_REPOS_FOR_BOUNCER_SHOWING =
-        unreleasedFlag("use_repos_for_bouncer_showing", teamfood = true)
+    val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing")
 
     // 3100 - Haptic interactions
 
     // TODO(b/290213663): Tracking Bug
     @JvmField
-    val ONE_WAY_HAPTICS_API_MIGRATION = unreleasedFlag("oneway_haptics_api_migration")
+    val ONE_WAY_HAPTICS_API_MIGRATION =
+            unreleasedFlag("oneway_haptics_api_migration", teamfood = true)
 
     /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
     @JvmField
@@ -783,11 +779,11 @@
 
     /** Enable the Compose implementation of the PeopleSpaceActivity. */
     @JvmField
-    val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space", teamfood = true)
+    val COMPOSE_PEOPLE_SPACE = releasedFlag("compose_people_space")
 
     /** Enable the Compose implementation of the Quick Settings footer actions. */
     @JvmField
-    val COMPOSE_QS_FOOTER_ACTIONS = unreleasedFlag("compose_qs_footer_actions", teamfood = true)
+    val COMPOSE_QS_FOOTER_ACTIONS = releasedFlag("compose_qs_footer_actions")
 
     /** Enable the share wifi button in Quick Settings internet dialog. */
     @JvmField
@@ -804,4 +800,9 @@
     /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
     @JvmField
     val BLUETOOTH_QS_TILE_DIALOG = unreleasedFlag("bluetooth_qs_tile_dialog")
+
+    // TODO(b/300995746): Tracking Bug
+    /** Enable communal hub features. */
+    @JvmField
+    val COMMUNAL_HUB = resourceBooleanFlag(R.bool.config_communalServiceEnabled, "communal_hub")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 257006e..22bcf0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -18,10 +18,8 @@
 package com.android.systemui.keyguard
 
 import android.content.Context
-import android.content.res.Configuration
 import android.view.LayoutInflater
 import android.view.View
-import android.view.ViewGroup
 import com.android.keyguard.KeyguardStatusView
 import com.android.keyguard.KeyguardStatusViewController
 import com.android.keyguard.LockIconView
@@ -29,38 +27,21 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent
 import com.android.systemui.CoreStartable
 import com.android.systemui.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
-import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import javax.inject.Inject
@@ -74,30 +55,17 @@
 @Inject
 constructor(
     private val keyguardRootView: KeyguardRootView,
-    private val sharedNotificationContainer: SharedNotificationContainer,
     private val keyguardRootViewModel: KeyguardRootViewModel,
     private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
-    private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
-    private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
     private val notificationShadeWindowView: NotificationShadeWindowView,
     private val featureFlags: FeatureFlags,
     private val indicationController: KeyguardIndicationController,
-    private val keyguardQuickAffordancesCombinedViewModel:
-        KeyguardQuickAffordancesCombinedViewModel,
-    private val falsingManager: FalsingManager,
-    private val vibratorHelper: VibratorHelper,
     private val keyguardStateController: KeyguardStateController,
-    private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
-    private val activityStarter: ActivityStarter,
     private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
     private val chipbarCoordinator: ChipbarCoordinator,
     private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
     private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
     private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
-    private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
-    private val communalWidgetViewModel: CommunalWidgetViewModel,
-    private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
-    private val notificationStackScrollerLayoutController: NotificationStackScrollLayoutController,
     private val context: Context,
     private val keyguardIndicationController: KeyguardIndicationController,
     private val lockIconViewController: LockIconViewController,
@@ -105,10 +73,7 @@
 
     private var rootViewHandle: DisposableHandle? = null
     private var indicationAreaHandle: DisposableHandle? = null
-    private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
-    private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
-    private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
-    private var settingsPopupMenuHandle: DisposableHandle? = null
+
     var keyguardStatusViewController: KeyguardStatusViewController? = null
         get() {
             if (field == null) {
@@ -127,52 +92,12 @@
 
     override fun start() {
         bindKeyguardRootView()
-        if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
-            keyguardRootView.removeAllViews()
-            initializeViews()
-        } else {
-            val notificationPanel =
-                notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
-            unbindKeyguardBottomArea(notificationPanel)
-            bindIndicationArea()
-            bindLockIconView(notificationPanel)
-            bindKeyguardStatusView(notificationPanel)
-            setupNotificationStackScrollLayout(notificationPanel)
-            bindLeftShortcut()
-            bindRightShortcut()
-            bindAmbientIndicationArea()
-            bindSettingsPopupMenu()
-            bindCommunalWidgetArea()
-        }
+        initializeViews()
 
         KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
         keyguardBlueprintCommandListener.start()
     }
 
-    fun setupNotificationStackScrollLayout(legacyParent: ViewGroup) {
-        if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
-            // This moves the existing NSSL view to a different parent, as the controller is a
-            // singleton and recreating it has other bad side effects
-            val nssl =
-                legacyParent.requireViewById<View>(R.id.notification_stack_scroller).also {
-                    (it.getParent() as ViewGroup).removeView(it)
-                }
-            sharedNotificationContainer.addNotificationStackScrollLayout(nssl)
-            SharedNotificationContainerBinder.bind(
-                sharedNotificationContainer,
-                sharedNotificationContainerViewModel,
-                notificationStackScrollerLayoutController,
-            )
-        }
-    }
-
-    override fun onConfigurationChanged(newConfig: Configuration?) {
-        super.onConfigurationChanged(newConfig)
-        leftShortcutHandle?.onConfigurationChanged()
-        rightShortcutHandle?.onConfigurationChanged()
-        ambientIndicationAreaHandle?.onConfigurationChanged()
-    }
-
     fun bindIndicationArea() {
         indicationAreaHandle?.dispose()
 
@@ -213,135 +138,6 @@
             )
     }
 
-    private fun bindLockIconView(legacyParent: ViewGroup) {
-        if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
-            legacyParent.requireViewById<View>(R.id.lock_icon_view).let {
-                legacyParent.removeView(it)
-            }
-        } else {
-            keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let {
-                keyguardRootView.removeView(it)
-            }
-            legacyParent.requireViewById<LockIconView>(R.id.lock_icon_view).let {
-                lockIconViewController.setLockIconView(it)
-            }
-        }
-    }
-
-    private fun bindAmbientIndicationArea() {
-        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            ambientIndicationAreaHandle?.destroy()
-            ambientIndicationAreaHandle =
-                KeyguardAmbientIndicationAreaViewBinder.bind(
-                    notificationShadeWindowView,
-                    keyguardAmbientIndicationViewModel,
-                    keyguardRootViewModel,
-                )
-        } else {
-            keyguardRootView.findViewById<View?>(R.id.ambient_indication_container)?.let {
-                keyguardRootView.removeView(it)
-            }
-        }
-    }
-
-    private fun bindSettingsPopupMenu() {
-        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            settingsPopupMenuHandle?.dispose()
-            settingsPopupMenuHandle =
-                KeyguardSettingsViewBinder.bind(
-                    keyguardRootView,
-                    keyguardSettingsMenuViewModel,
-                    vibratorHelper,
-                    activityStarter,
-                )
-        } else {
-            keyguardRootView.findViewById<View?>(R.id.keyguard_settings_button)?.let {
-                keyguardRootView.removeView(it)
-            }
-        }
-    }
-
-    private fun unbindKeyguardBottomArea(legacyParent: ViewGroup) {
-        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            legacyParent.requireViewById<View>(R.id.keyguard_bottom_area).let {
-                legacyParent.removeView(it)
-            }
-        }
-    }
-
-    private fun bindLeftShortcut() {
-        leftShortcutHandle?.destroy()
-        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            leftShortcutHandle =
-                KeyguardQuickAffordanceViewBinder.bind(
-                    keyguardRootView.requireViewById(R.id.start_button),
-                    keyguardQuickAffordancesCombinedViewModel.startButton,
-                    keyguardRootViewModel.alpha,
-                    falsingManager,
-                    vibratorHelper,
-                ) {
-                    indicationController.showTransientIndication(it)
-                }
-        } else {
-            keyguardRootView.findViewById<View?>(R.id.start_button)?.let {
-                keyguardRootView.removeView(it)
-            }
-        }
-    }
-
-    private fun bindRightShortcut() {
-        rightShortcutHandle?.destroy()
-        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            rightShortcutHandle =
-                KeyguardQuickAffordanceViewBinder.bind(
-                    keyguardRootView.requireViewById(R.id.end_button),
-                    keyguardQuickAffordancesCombinedViewModel.endButton,
-                    keyguardRootViewModel.alpha,
-                    falsingManager,
-                    vibratorHelper,
-                ) {
-                    indicationController.showTransientIndication(it)
-                }
-        } else {
-            keyguardRootView.findViewById<View?>(R.id.end_button)?.let {
-                keyguardRootView.removeView(it)
-            }
-        }
-    }
-
-    fun bindKeyguardStatusView(legacyParent: ViewGroup) {
-        // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
-        // Disable one of them
-        if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
-            legacyParent.findViewById<View>(R.id.keyguard_status_view)?.let {
-                legacyParent.removeView(it)
-            }
-
-            val keyguardStatusView = keyguardRootView.addStatusView()
-            val statusViewComponent = keyguardStatusViewComponentFactory.build(keyguardStatusView)
-            val controller = statusViewComponent.getKeyguardStatusViewController()
-            controller.init()
-            keyguardStatusViewController = controller
-        } else {
-            keyguardRootView.findViewById<View?>(R.id.keyguard_status_view)?.let {
-                keyguardRootView.removeView(it)
-            }
-        }
-    }
-
-    private fun bindCommunalWidgetArea() {
-        if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
-            return
-        }
-
-        CommunalWidgetViewBinder.bind(
-            keyguardRootView,
-            communalWidgetViewModel,
-            communalWidgetViewAdapter,
-            keyguardBlueprintInteractor,
-        )
-    }
-
     /**
      * Temporary, to allow NotificationPanelViewController to use the same instance while code is
      * migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2b4dc81..394bed4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1968,6 +1968,10 @@
             mExternallyEnabled = enabled;
 
             if (!enabled && mShowing) {
+                if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+                    Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown");
+                    return;
+                }
                 // hiding keyguard that is showing, remember to reshow later
                 if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
                         + "disabling status bar expansion");
@@ -2193,7 +2197,7 @@
      * Enable the keyguard if the settings are appropriate.
      */
     private void doKeyguardLocked(Bundle options) {
-        // if another app is disabling us, don't show unless we're in lockdown mode
+        // if another app is disabling us, don't show
         if (!mExternallyEnabled
                 && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
@@ -2208,14 +2212,21 @@
         // we explicitly re-set state.
         if (mShowing && mKeyguardStateController.isShowing()) {
             if (mPM.isInteractive() && !mHiding) {
-                // It's already showing, and we're not trying to show it while the screen is off.
-                // We can simply reset all of the views, but don't hide the bouncer in case the user
-                // is currently interacting with it.
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing (instead, resetting) because it is "
-                        + "already showing, we're interactive, and we were not previously hiding. "
-                        + "It should be safe to short-circuit here.");
-                resetStateLocked(/* hideBouncer= */ false);
-                return;
+                if (mKeyguardStateController.isKeyguardGoingAway()) {
+                    Log.e(TAG, "doKeyguard: we're still showing, but going away. Re-show the "
+                            + "keyguard rather than short-circuiting and resetting.");
+                } else {
+                    // It's already showing, and we're not trying to show it while the screen is
+                    // off. We can simply reset all of the views, but don't hide the bouncer in case
+                    // the user is currently interacting with it.
+                    if (DEBUG) Log.d(TAG,
+                            "doKeyguard: not showing (instead, resetting) because it is "
+                                    + "already showing, we're interactive, we were not "
+                                    + "previously hiding. It should be safe to short-circuit "
+                                    + "here.");
+                    resetStateLocked(/* hideBouncer= */ false);
+                    return;
+                }
             } else {
                 // We are trying to show the keyguard while the screen is off or while we were in
                 // the middle of hiding - this results from race conditions involving locking while
@@ -2740,13 +2751,18 @@
             setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.SHOW);
             setPendingLock(false);
 
-            // Force if we we're showing in the middle of hiding, to ensure we end up in the correct
-            // state.
-            setShowingLocked(true, mHiding /* force */);
-            if (mHiding) {
-                Log.d(TAG, "Forcing setShowingLocked because mHiding=true, which means we're "
-                        + "showing in the middle of hiding.");
+            final boolean hidingOrGoingAway =
+                    mHiding || mKeyguardStateController.isKeyguardGoingAway();
+            if (hidingOrGoingAway) {
+                Log.d(TAG, "Forcing setShowingLocked because one of these is true:"
+                        + "mHiding=" + mHiding
+                        + ", keyguardGoingAway=" + mKeyguardStateController.isKeyguardGoingAway()
+                        + ", which means we're showing in the middle of hiding.");
             }
+
+            // Force if we we're showing in the middle of unlocking, to ensure we end up in the
+            // correct state.
+            setShowingLocked(true, hidingOrGoingAway /* force */);
             mHiding = false;
 
             if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
@@ -2954,7 +2970,6 @@
         Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                 + " fadeoutDuration=" + fadeoutDuration);
         synchronized (KeyguardViewMediator.this) {
-
             // Tell ActivityManager that we canceled the keyguard animation if
             // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard,
             // unless we're animating the surface behind the keyguard and will be hiding the
@@ -3222,10 +3237,13 @@
 
         // Post layout changes to the next frame, so we don't hang at the end of the animation.
         DejankUtils.postAfterTraversal(() -> {
-            if (!mPM.isInteractive()) {
-                Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal" +
-                        "Not interactive after traversal. Don't hide the keyguard. This means we " +
-                        "re-locked the device during unlock.");
+            if (!mPM.isInteractive() && !mPendingLock) {
+                Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal:"
+                        + "mPM.isInteractive()=" + mPM.isInteractive()
+                        + "mPendingLock=" + mPendingLock + "."
+                        + "One of these being false means we re-locked the device during unlock. "
+                        + "Do not proceed to finish keyguard exit and unlock.");
+                finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
                 return;
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 13dfe24..03c166c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.data.repository.CommunalRepositoryModule;
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -93,6 +94,7 @@
         KeyguardStatusViewComponent.class,
         KeyguardUserSwitcherComponent.class},
         includes = {
+            CommunalRepositoryModule.class,
             CommunalWidgetRepositoryModule.class,
             FalsingModule.class,
             KeyguardDataQuickAffordanceModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index af0abdf..5e5caba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -34,7 +34,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.withContext
 
 @SysUISingleton
@@ -58,16 +58,21 @@
         get() = R.drawable.ic_camera
 
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
-        get() =
-            flowOf(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon =
-                        Icon.Resource(
-                            R.drawable.ic_camera,
-                            ContentDescription.Resource(R.string.accessibility_camera_button)
-                        )
-                )
+        get() = flow {
+            emit(
+                if (isLaunchable()) {
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                        icon =
+                            Icon.Resource(
+                                R.drawable.ic_camera,
+                                ContentDescription.Resource(R.string.accessibility_camera_button)
+                            )
+                    )
+                } else {
+                    KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+                }
             )
+        }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
         return if (isLaunchable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 5d7a3d4..23f50ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.wallet.controller.QuickAccessWalletController
+import com.android.systemui.wallet.util.getPaymentCards
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
@@ -60,7 +61,7 @@
             val callback =
                 object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
                     override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
-                        val hasCards = response?.walletCards?.isNotEmpty() == true
+                        val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
                         trySendWithFailureLogging(
                             state(
                                 isFeatureEnabled = isWalletAvailable(),
@@ -135,7 +136,7 @@
                 object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
                     override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                         continuation.resumeWith(
-                            Result.success(response?.walletCards ?: emptyList())
+                            Result.success(getPaymentCards(response.walletCards) ?: emptyList())
                         )
                     }
 
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 6db21b2..233acd9 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
@@ -54,13 +54,13 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
+import com.google.errorprone.annotations.CompileTimeConstant
 import java.io.PrintWriter
 import java.util.Arrays
 import java.util.stream.Collectors
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.delay
@@ -112,33 +112,27 @@
     fun setLockedOut(isLockedOut: Boolean)
 
     /**
-     * Cancel current face authentication and prevent it from running until [resumeFaceAuth] is
-     * invoked.
-     */
-    fun pauseFaceAuth()
-
-    /**
-     * Allow face auth paused using [pauseFaceAuth] to run again. The next invocation to
-     * [authenticate] will run as long as other gating conditions don't stop it from running.
-     */
-    fun resumeFaceAuth()
-
-    /**
-     * Trigger face authentication.
+     * Request face authentication or detection to be run.
      *
      * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
      * ignored if face authentication is already running. Results should be propagated through
      * [authenticationStatus]
      *
      * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+     *
+     * Method returns immediately and the face auth request is processed as soon as possible.
      */
-    suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+    fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
 
     /** Stop currently running face authentication or detection. */
     fun cancel()
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
+private data class AuthenticationRequest(
+    val uiEvent: FaceAuthUiEvent,
+    val fallbackToDetection: Boolean
+)
+
 @SysUISingleton
 class DeviceEntryFaceAuthRepositoryImpl
 @Inject
@@ -171,6 +165,8 @@
     private var faceAcquiredInfoIgnoreList: Set<Int>
     private var retryCount = 0
 
+    private var pendingAuthenticateRequest = MutableStateFlow<AuthenticationRequest?>(null)
+
     private var cancelNotReceivedHandlerJob: Job? = null
     private var halErrorRetryJob: Job? = null
 
@@ -193,15 +189,6 @@
     override val isAuthRunning: StateFlow<Boolean>
         get() = _isAuthRunning
 
-    private val faceAuthPaused = MutableStateFlow(false)
-    override fun pauseFaceAuth() {
-        faceAuthPaused.value = true
-    }
-
-    override fun resumeFaceAuth() {
-        faceAuthPaused.value = false
-    }
-
     private val keyguardSessionId: InstanceId?
         get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
 
@@ -213,6 +200,8 @@
     override val isAuthenticated: Flow<Boolean>
         get() = _isAuthenticated
 
+    private var cancellationInProgress = MutableStateFlow(false)
+
     override val isBypassEnabled: Flow<Boolean> =
         keyguardBypassController?.let {
             conflatedCallbackFlow {
@@ -302,6 +291,7 @@
             observeFaceDetectGatingChecks()
             observeFaceAuthResettingConditions()
             listenForSchedulingWatchdog()
+            processPendingAuthRequests()
         } else {
             canRunFaceAuth = MutableStateFlow(false).asStateFlow()
             canRunDetection = MutableStateFlow(false).asStateFlow()
@@ -338,6 +328,7 @@
             )
             .onEach { anyOfThemIsTrue ->
                 if (anyOfThemIsTrue) {
+                    clearPendingAuthRequest("Resetting auth status")
                     _isAuthenticated.value = false
                     retryCount = 0
                     halErrorRetryJob?.cancel()
@@ -346,6 +337,15 @@
             .launchIn(applicationScope)
     }
 
+    private fun clearPendingAuthRequest(@CompileTimeConstant loggingContext: String) {
+        faceAuthLogger.clearingPendingAuthRequest(
+            loggingContext,
+            pendingAuthenticateRequest.value?.uiEvent,
+            pendingAuthenticateRequest.value?.fallbackToDetection
+        )
+        pendingAuthenticateRequest.value = null
+    }
+
     private fun observeFaceDetectGatingChecks() {
         canRunDetection
             .onEach {
@@ -378,7 +378,6 @@
                 biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
                 "isFaceAuthEnrolledAndEnabled"
             ),
-            Pair(faceAuthPaused.isFalse(), "faceAuthIsNotPaused"),
             Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
             Pair(
                 keyguardRepository.wakefulness.map { it.isStartingToSleep() }.isFalse(),
@@ -402,7 +401,13 @@
                 biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
                 "userHasNotLockedDownDevice"
             ),
-            Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing")
+            Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing"),
+            Pair(
+                userRepository.selectedUser
+                    .map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS }
+                    .isFalse(),
+                "userSwitchingInProgress"
+            )
         )
     }
 
@@ -440,9 +445,6 @@
                 }
                 _authenticationStatus.value = errorStatus
                 _isAuthenticated.value = false
-                if (errorStatus.isCancellationError()) {
-                    handleFaceCancellationError()
-                }
                 if (errorStatus.isHardwareError()) {
                     faceAuthLogger.hardwareError(errorStatus)
                     handleFaceHardwareError()
@@ -471,16 +473,6 @@
             }
         }
 
-    private fun handleFaceCancellationError() {
-        applicationScope.launch {
-            faceAuthRequestedWhileCancellation?.let {
-                faceAuthLogger.launchingQueuedFaceAuthRequest(it)
-                authenticate(it)
-            }
-            faceAuthRequestedWhileCancellation = null
-        }
-    }
-
     private fun handleFaceHardwareError() {
         if (retryCount < HAL_ERROR_RETRY_MAX) {
             retryCount++
@@ -490,7 +482,7 @@
                     delay(HAL_ERROR_RETRY_TIMEOUT)
                     if (retryCount < HAL_ERROR_RETRY_MAX) {
                         faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
-                        authenticate(
+                        requestAuthenticate(
                             FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
                             fallbackToDetection = false
                         )
@@ -501,7 +493,7 @@
 
     private fun onFaceAuthRequestCompleted() {
         cancelNotReceivedHandlerJob?.cancel()
-        cancellationInProgress = false
+        cancellationInProgress.value = false
         _isAuthRunning.value = false
         authCancellationSignal = null
     }
@@ -512,24 +504,60 @@
             _detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
         }
 
-    private var cancellationInProgress = false
-    private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+    override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+        if (pendingAuthenticateRequest.value != null) {
+            faceAuthLogger.ignoredFaceAuthTrigger(
+                pendingAuthenticateRequest.value?.uiEvent,
+                "Previously queued trigger skipped due to new request"
+            )
+        }
+        faceAuthLogger.queueingRequest(uiEvent, fallbackToDetection)
+        pendingAuthenticateRequest.value = AuthenticationRequest(uiEvent, fallbackToDetection)
+    }
 
-    override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+    private fun processPendingAuthRequests() {
+        combine(
+                pendingAuthenticateRequest,
+                canRunFaceAuth,
+                canRunDetection,
+                cancellationInProgress,
+            ) { pending, canRunAuth, canRunDetect, cancelInProgress ->
+                if (
+                    pending != null &&
+                        !(canRunAuth || (canRunDetect && pending.fallbackToDetection)) ||
+                        cancelInProgress
+                ) {
+                    faceAuthLogger.notProcessingRequestYet(
+                        pending?.uiEvent,
+                        canRunAuth,
+                        canRunDetect,
+                        cancelInProgress
+                    )
+                    return@combine null
+                } else {
+                    return@combine pending
+                }
+            }
+            .onEach {
+                it?.let {
+                    faceAuthLogger.processingRequest(it.uiEvent, it.fallbackToDetection)
+                    clearPendingAuthRequest("Authenticate was invoked")
+                    authenticate(it.uiEvent, it.fallbackToDetection)
+                }
+            }
+            .flowOn(mainDispatcher)
+            .launchIn(applicationScope)
+    }
+
+    private suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
         if (_isAuthRunning.value) {
             faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "face auth is currently running")
             return
         }
 
-        if (cancellationInProgress) {
-            faceAuthLogger.queuingRequestWhileCancelling(
-                faceAuthRequestedWhileCancellation,
-                uiEvent
-            )
-            faceAuthRequestedWhileCancellation = uiEvent
+        if (cancellationInProgress.value) {
+            faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "cancellation in progress")
             return
-        } else {
-            faceAuthRequestedWhileCancellation = null
         }
 
         if (canRunFaceAuth.value) {
@@ -553,12 +581,19 @@
                     FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
                 )
             }
-        } else if (fallbackToDetection && canRunDetection.value) {
-            faceAuthLogger.ignoredFaceAuthTrigger(
-                uiEvent,
-                "face auth gating check is false, falling back to detection."
-            )
-            detect()
+        } else if (canRunDetection.value) {
+            if (fallbackToDetection) {
+                faceAuthLogger.ignoredFaceAuthTrigger(
+                    uiEvent,
+                    "face auth gating check is false, falling back to detection."
+                )
+                detect()
+            } else {
+                faceAuthLogger.ignoredFaceAuthTrigger(
+                    uiEvent = uiEvent,
+                    "face auth gating check is false and fallback to detection is not requested"
+                )
+            }
         } else {
             faceAuthLogger.ignoredFaceAuthTrigger(
                 uiEvent,
@@ -608,13 +643,13 @@
                 faceAuthLogger.cancelSignalNotReceived(
                     _isAuthRunning.value,
                     _isLockedOut.value,
-                    cancellationInProgress,
-                    faceAuthRequestedWhileCancellation
+                    cancellationInProgress.value,
+                    pendingAuthenticateRequest.value?.uiEvent
                 )
                 _authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError()
                 onFaceAuthRequestCompleted()
             }
-        cancellationInProgress = true
+        cancellationInProgress.value = true
         _isAuthRunning.value = false
     }
 
@@ -647,9 +682,7 @@
             "    supportsFaceDetection: " +
                 "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
         )
-        pw.println(
-            "  faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
-        )
+        pw.println("  _pendingAuthenticateRequest: ${pendingAuthenticateRequest.value}")
         pw.println("  authCancellationSignal: $authCancellationSignal")
         pw.println("  detectCancellationSignal: $detectCancellationSignal")
         pw.println("  faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index 46135fa..c8cb9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -56,9 +56,6 @@
         get() = emptyFlow()
 
     override fun setLockedOut(isLockedOut: Boolean) = Unit
-    override fun pauseFaceAuth() = Unit
-
-    override fun resumeFaceAuth() = Unit
 
     /**
      * Trigger face authentication.
@@ -69,7 +66,7 @@
      *
      * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
      */
-    override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {}
+    override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) = Unit
 
     /** Stop currently running face authentication or detection. */
     override fun cancel() {}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
index ca430da..a257f52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -172,7 +172,6 @@
 
     private fun isFeatureEnabled(): Boolean {
         return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
-            featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI) &&
             appContext.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9c796f8..7f43cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -380,10 +380,6 @@
     suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
         return listOf(
             KeyguardPickerFlag(
-                name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
-                value = featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI),
-            ),
-            KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
                 value =
                     !isFeatureDisabledByDevicePolicy() &&
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 ccc2080..f0df3a2e 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
@@ -52,8 +52,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
 import kotlinx.coroutines.yield
 
 /**
@@ -144,14 +142,11 @@
             .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) {
+                if (wasSwitching && !isSwitching) {
                     val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
                     repository.setLockedOut(
                         lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED
                     )
-                    repository.resumeFaceAuth()
                     yield()
                     runFaceAuth(
                         FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
@@ -232,12 +227,8 @@
                     )
             } else {
                 faceAuthenticationStatusOverride.value = null
-                applicationScope.launch {
-                    withContext(mainDispatcher) {
-                        faceAuthenticationLogger.authRequested(uiEvent)
-                        repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
-                    }
-                }
+                faceAuthenticationLogger.authRequested(uiEvent)
+                repository.requestAuthenticate(uiEvent, fallbackToDetection = fallbackToDetect)
             }
         } else {
             faceAuthenticationLogger.ignoredFaceAuthTrigger(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 44acf4f..a9c71ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -79,12 +79,6 @@
     //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt]
     @Deprecated("Deprecated as part of b/278057014")
     interface Binding {
-        /**
-         * Returns a collection of [ViewPropertyAnimator] instances that can be used to animate the
-         * indication areas.
-         */
-        fun getIndicationAreaAnimators(): List<ViewPropertyAnimator>
-
         /** Notifies that device configuration has changed. */
         fun onConfigurationChanged()
 
@@ -281,10 +275,6 @@
             }
 
         return object : Binding {
-            override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> {
-                return listOf(ambientIndicationArea).mapNotNull { it?.animate() }
-            }
-
             override fun onConfigurationChanged() {
                 configurationBasedDimensions.value = loadFromResources(view)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index a948741..f2b28d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,123 +19,14 @@
 
 import android.content.Context
 import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.ImageView
 import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.content.res.ResourcesCompat
-import com.android.keyguard.KeyguardStatusView
-import com.android.keyguard.LockIconView
-import com.android.systemui.R
-import com.android.systemui.animation.view.LaunchableImageView
 
 /** Provides a container for all keyguard ui content. */
 class KeyguardRootView(
     context: Context,
-    private val attrs: AttributeSet?,
+    attrs: AttributeSet?,
 ) :
     ConstraintLayout(
         context,
         attrs,
-    ) {
-
-    private var statusView: KeyguardStatusView? = null
-
-    init {
-        addIndicationTextArea()
-        addLockIconView()
-        addAmbientIndicationArea()
-        addLeftShortcut()
-        addRightShortcut()
-        addSettingsPopupMenu()
-        addStatusView()
-    }
-
-    private fun addIndicationTextArea() {
-        val view = KeyguardIndicationArea(context, attrs)
-        addView(view)
-    }
-
-    private fun addLockIconView() {
-        val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
-        addView(view)
-    }
-
-    private fun addAmbientIndicationArea() {
-        LayoutInflater.from(context).inflate(R.layout.ambient_indication, this)
-    }
-
-    private fun addLeftShortcut() {
-        val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
-        val view =
-            LaunchableImageView(context, attrs).apply {
-                id = R.id.start_button
-                scaleType = ImageView.ScaleType.FIT_CENTER
-                background =
-                    ResourcesCompat.getDrawable(
-                        context.resources,
-                        R.drawable.keyguard_bottom_affordance_bg,
-                        context.theme
-                    )
-                foreground =
-                    ResourcesCompat.getDrawable(
-                        context.resources,
-                        R.drawable.keyguard_bottom_affordance_selected_border,
-                        context.theme
-                    )
-                visibility = View.INVISIBLE
-                setPadding(padding, padding, padding, padding)
-            }
-        addView(view)
-    }
-
-    private fun addRightShortcut() {
-        val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
-        val view =
-            LaunchableImageView(context, attrs).apply {
-                id = R.id.end_button
-                scaleType = ImageView.ScaleType.FIT_CENTER
-                background =
-                    ResourcesCompat.getDrawable(
-                        context.resources,
-                        R.drawable.keyguard_bottom_affordance_bg,
-                        context.theme
-                    )
-                foreground =
-                    ResourcesCompat.getDrawable(
-                        context.resources,
-                        R.drawable.keyguard_bottom_affordance_selected_border,
-                        context.theme
-                    )
-                visibility = View.INVISIBLE
-                setPadding(padding, padding, padding, padding)
-            }
-        addView(view)
-    }
-
-    private fun addSettingsPopupMenu() {
-        val view =
-            LayoutInflater.from(context)
-                .inflate(R.layout.keyguard_settings_popup_menu, this, false)
-                .apply {
-                    id = R.id.keyguard_settings_button
-                    visibility = GONE
-                }
-        addView(view)
-    }
-
-    fun addStatusView(): KeyguardStatusView {
-        // StatusView may need to be rebuilt on config changes. Remove and reinflate
-        statusView?.let { removeView(it) }
-        val view =
-            (LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, this, false)
-                    as KeyguardStatusView)
-                .apply {
-                    setClipChildren(false)
-                    statusView = this
-                }
-
-        addView(view)
-        return view
-    }
-}
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 15bb909..1eeb017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,10 +17,7 @@
 
 package com.android.systemui.keyguard.ui.view.layout.blueprints
 
-import androidx.constraintlayout.widget.ConstraintLayout
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
@@ -29,6 +26,7 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
 import javax.inject.Inject
@@ -49,10 +47,10 @@
     defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
     defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
     defaultStatusViewSection: DefaultStatusViewSection,
+    defaultStatusBarSection: DefaultStatusBarSection,
     defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
     splitShadeGuidelines: SplitShadeGuidelines,
     aodNotificationIconsSection: AodNotificationIconsSection,
-    private val featureFlags: FeatureFlags,
 ) : KeyguardBlueprint {
     override val id: String = DEFAULT
 
@@ -64,21 +62,12 @@
             defaultAmbientIndicationAreaSection,
             defaultSettingsPopupMenuSection,
             defaultStatusViewSection,
+            defaultStatusBarSection,
             defaultNotificationStackScrollLayoutSection,
             splitShadeGuidelines,
             aodNotificationIconsSection,
         )
 
-    override fun replaceViews(
-        previousBlueprint: KeyguardBlueprint?,
-        constraintLayout: ConstraintLayout,
-        bindData: Boolean
-    ) {
-        if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
-            super.replaceViews(previousBlueprint, constraintLayout, bindData)
-        }
-    }
-
     companion object {
         const val DEFAULT = "default"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index 6534dcf..a9885fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
 import javax.inject.Inject
@@ -41,6 +42,7 @@
     defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
     alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
     defaultStatusViewSection: DefaultStatusViewSection,
+    defaultStatusBarSection: DefaultStatusBarSection,
     splitShadeGuidelines: SplitShadeGuidelines,
     defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
     aodNotificationIconsSection: AodNotificationIconsSection,
@@ -55,6 +57,7 @@
             defaultSettingsPopupMenuSection,
             alignShortcutsToUdfpsSection,
             defaultStatusViewSection,
+            defaultStatusBarSection,
             defaultNotificationStackScrollLayoutSection,
             splitShadeGuidelines,
             aodNotificationIconsSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 9c6e953..100099d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -73,7 +73,13 @@
         val mBottomPaddingPx =
             context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
         val bounds = windowManager.currentWindowMetrics.bounds
-        val widthPixels = bounds.right.toFloat()
+        val insets = windowManager.currentWindowMetrics.windowInsets
+        var widthPixels = bounds.right.toFloat()
+        if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
+            // Assumed to be initially neglected as there are no left or right insets in portrait.
+            // However, on landscape, these insets need to included when calculating the midpoint.
+            widthPixels -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat()
+        }
         val heightPixels = bounds.bottom.toFloat()
         val defaultDensity =
             DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
new file mode 100644
index 0000000..d6c69b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusBarSection.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.dagger.KeyguardStatusBarViewComponent
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeViewStateProvider
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+/** A section for the status bar displayed at the top of the lockscreen. */
+class DefaultStatusBarSection
+@Inject
+constructor(
+    private val context: Context,
+    private val featureFlags: FeatureFlags,
+    private val notificationPanelView: NotificationPanelView,
+    private val keyguardStatusBarViewComponentFactory: KeyguardStatusBarViewComponent.Factory,
+) : KeyguardSection() {
+
+    private val statusBarViewId = R.id.keyguard_header
+
+    override fun addViews(constraintLayout: ConstraintLayout) {
+        if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+            return
+        }
+
+        notificationPanelView.findViewById<View>(statusBarViewId)?.let {
+            (it.parent as ViewGroup).removeView(it)
+        }
+
+        val view =
+            LayoutInflater.from(constraintLayout.context)
+                .inflate(R.layout.keyguard_status_bar, constraintLayout, false)
+                as KeyguardStatusBarView
+
+        constraintLayout.addView(view)
+    }
+
+    override fun bindData(constraintLayout: ConstraintLayout) {
+        if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+            return
+        }
+
+        val statusBarView =
+            constraintLayout.findViewById<KeyguardStatusBarView>(statusBarViewId) ?: return
+
+        val provider =
+            object : ShadeViewStateProvider {
+                override val lockscreenShadeDragProgress: Float = 0f
+                override val panelViewExpandedHeight: Float = 0f
+                override fun shouldHeadsUpBeVisible(): Boolean {
+                    return false
+                }
+            }
+        val statusBarViewComponent =
+            keyguardStatusBarViewComponentFactory.build(statusBarView, provider)
+        val controller = statusBarViewComponent.keyguardStatusBarViewController
+        controller.init()
+    }
+
+    override fun applyConstraints(constraintSet: ConstraintSet) {
+        constraintSet.apply {
+            constrainHeight(statusBarViewId, Utils.getStatusBarHeaderHeightKeyguard(context))
+            connect(statusBarViewId, TOP, PARENT_ID, TOP)
+            connect(statusBarViewId, START, PARENT_ID, START)
+            connect(statusBarViewId, END, PARENT_ID, END)
+        }
+    }
+
+    override fun removeViews(constraintLayout: ConstraintLayout) {
+        constraintLayout.removeView(statusBarViewId)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index b1dd373..1cb10bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -39,7 +39,7 @@
 import com.android.systemui.media.controls.ui.KeyguardMediaController
 import com.android.systemui.shade.NotificationPanelView
 import com.android.systemui.shade.NotificationPanelViewController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Utils
 import dagger.Lazy
 import javax.inject.Inject
@@ -55,6 +55,7 @@
     private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>,
     private val notificationPanelViewController: Lazy<NotificationPanelViewController>,
     private val keyguardMediaController: KeyguardMediaController,
+    private val splitShadeStateController: SplitShadeStateController
 ) : KeyguardSection() {
     private val statusViewId = R.id.keyguard_status_view
 
@@ -90,7 +91,7 @@
                     it.requireViewById<ViewGroup>(R.id.status_view_media_container)
                 )
                 keyguardViewConfigurator.get().keyguardStatusViewController = controller
-                notificationPanelViewController.get().updateStatusBarViewController()
+                notificationPanelViewController.get().updateStatusViewController()
             }
         }
     }
@@ -99,12 +100,14 @@
         constraintSet.apply {
             constrainWidth(statusViewId, MATCH_CONSTRAINT)
             constrainHeight(statusViewId, WRAP_CONTENT)
+            // TODO(b/296122465): Constrain to the top of [DefaultStatusBarSection] and remove the
+            // extra margin below.
             connect(statusViewId, TOP, PARENT_ID, TOP)
             connect(statusViewId, START, PARENT_ID, START)
             connect(statusViewId, END, PARENT_ID, END)
 
             val margin =
-                if (LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)) {
+                if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) {
                     context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
                 } else {
                     context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
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 05c9323..91b3357 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
@@ -17,27 +17,50 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
 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.flow.Flow
+import kotlinx.coroutines.CoroutineScope
+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,
+    communalInteractor: CommunalInteractor,
     val longPress: KeyguardLongPressViewModel,
 ) {
     /** The key of the scene we should switch to when swiping up. */
-    val upDestinationSceneKey: Flow<SceneKey> =
-        authenticationInteractor.isUnlocked.map { isUnlocked ->
-            if (isUnlocked) {
-                SceneKey.Gone
-            } else {
-                SceneKey.Bouncer
-            }
+    val upDestinationSceneKey: StateFlow<SceneKey> =
+        authenticationInteractor.isUnlocked
+            .map { isUnlocked -> upDestinationSceneKey(isUnlocked) }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = upDestinationSceneKey(authenticationInteractor.isUnlocked.value),
+            )
+
+    private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey {
+        return if (isUnlocked) {
+            SceneKey.Gone
+        } else {
+            SceneKey.Bouncer
+        }
+    }
+
+    /** The key of the scene we should switch to when swiping left. */
+    val leftDestinationSceneKey: SceneKey? =
+        if (communalInteractor.isCommunalEnabled) {
+            SceneKey.Communal
+        } else {
+            null
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 66067b1..8143f99 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -29,36 +29,18 @@
 constructor(
     @FaceAuthLog private val logBuffer: LogBuffer,
 ) {
-    fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent, ignoredReason: String) {
+    fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent?, ignoredReason: String) {
         logBuffer.log(
             TAG,
             DEBUG,
             {
-                str1 = uiEvent.reason
+                str1 = "${uiEvent?.reason}"
                 str2 = ignoredReason
             },
             { "Ignoring trigger because $str2, Trigger reason: $str1" }
         )
     }
 
-    fun queuingRequestWhileCancelling(
-        alreadyQueuedRequest: FaceAuthUiEvent?,
-        newRequest: FaceAuthUiEvent
-    ) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            {
-                str1 = alreadyQueuedRequest?.reason
-                str2 = newRequest.reason
-            },
-            {
-                "Face auth requested while previous request is being cancelled, " +
-                    "already queued request: $str1 queueing the new request: $str2"
-            }
-        )
-    }
-
     fun authenticating(uiEvent: FaceAuthUiEvent) {
         logBuffer.log(TAG, DEBUG, { str1 = uiEvent.reason }, { "Running authenticate for $str1" })
     }
@@ -161,15 +143,6 @@
         )
     }
 
-    fun launchingQueuedFaceAuthRequest(faceAuthRequestedWhileCancellation: FaceAuthUiEvent?) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            { str1 = "${faceAuthRequestedWhileCancellation?.reason}" },
-            { "Received cancellation error and starting queued face auth request: $str1" }
-        )
-    }
-
     fun faceAuthSuccess(result: FaceManager.AuthenticationResult) {
         logBuffer.log(
             TAG,
@@ -182,31 +155,10 @@
         )
     }
 
-    fun observedConditionChanged(newValue: Boolean, context: String) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            {
-                bool1 = newValue
-                str1 = context
-            },
-            { "Observed condition changed: $str1, new value: $bool1" }
-        )
-    }
-
     fun canFaceAuthRunChanged(canRun: Boolean) {
         logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
     }
 
-    fun canRunDetectionChanged(canRunDetection: Boolean) {
-        logBuffer.log(
-            TAG,
-            DEBUG,
-            { bool1 = canRunDetection },
-            { "canRunDetection value changed to $bool1" }
-        )
-    }
-
     fun cancellingFaceAuth() {
         logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
     }
@@ -236,7 +188,7 @@
         logBuffer.log(
             TAG,
             DEBUG,
-            { str1 = "$uiEvent" },
+            { str1 = uiEvent.reason },
             { "Requesting face auth for trigger: $str1" }
         )
     }
@@ -269,4 +221,77 @@
     fun faceLockedOut(@CompileTimeConstant reason: String) {
         logBuffer.log(TAG, DEBUG, "Face auth has been locked out: $reason")
     }
+
+    fun queueingRequest(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = "$uiEvent"
+                bool1 = fallbackToDetection
+            },
+            { "Queueing $str1 request for face auth, fallbackToDetection: $bool1" }
+        )
+    }
+
+    fun notProcessingRequestYet(
+        uiEvent: FaceAuthUiEvent?,
+        canRunAuth: Boolean,
+        canRunDetect: Boolean,
+        cancelInProgress: Boolean
+    ) {
+        uiEvent?.let {
+            logBuffer.log(
+                TAG,
+                DEBUG,
+                {
+                    str1 = uiEvent.reason
+                    bool1 = canRunAuth
+                    bool2 = canRunDetect
+                    bool3 = cancelInProgress
+                },
+                {
+                    "Waiting to process request: reason: $str1, " +
+                        "canRunAuth: $bool1, " +
+                        "canRunDetect: $bool2, " +
+                        "cancelInProgress: $bool3"
+                }
+            )
+        }
+    }
+
+    fun processingRequest(uiEvent: FaceAuthUiEvent?, fallbackToDetection: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = "${uiEvent?.reason}"
+                bool1 = fallbackToDetection
+            },
+            { "Processing face auth request: $str1, fallbackToDetect: $bool1" }
+        )
+    }
+
+    fun clearingPendingAuthRequest(
+        @CompileTimeConstant loggingContext: String,
+        uiEvent: FaceAuthUiEvent?,
+        fallbackToDetection: Boolean?
+    ) {
+        uiEvent?.let {
+            logBuffer.log(
+                TAG,
+                DEBUG,
+                {
+                    str1 = uiEvent.reason
+                    str2 = "$fallbackToDetection"
+                    str3 = loggingContext
+                },
+                {
+                    "Clearing pending auth: $str1, " +
+                        "fallbackToDetection: $str2, " +
+                        "reason: $str3"
+                }
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 60fd104..be48756 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -91,7 +91,13 @@
 
     public override fun onCreate(bundle: Bundle?) {
         lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
-        component = componentFactory.create(activity = this, view = this, resultHandler = this)
+        component =
+            componentFactory.create(
+                hostUserHandle = hostUserHandle,
+                callingPackage = callingPackage,
+                view = this,
+                resultHandler = this
+            )
         component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
 
         // Create a separate configuration controller for this activity as the configuration
@@ -274,7 +280,9 @@
             recentsViewController.hasRecentTasks
         }
 
-    override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
+    override fun shouldShowStickyContentPreviewWhenEmpty() = shouldShowContentPreview()
+
+    override fun shouldShowServiceTargets() = false
 
     private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1
 
@@ -286,6 +294,18 @@
     override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
         recentsViewController.createView(parent)
 
+    private val hostUserHandle: UserHandle
+        get() {
+            val extras =
+                intent.extras
+                    ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
+            return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
+                ?: error(
+                    "MediaProjectionAppSelectorActivity should be provided with " +
+                        "$EXTRA_HOST_APP_USER_HANDLE extra"
+                )
+        }
+
     companion object {
         const val TAG = "MediaProjectionAppSelectorActivity"
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index dddbeda..e79fc74 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -22,12 +22,14 @@
 import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME
 import android.app.PendingIntent
 import android.app.StatusBarManager
+import android.app.UriGrantsManager
 import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceConfig
 import android.app.smartspace.SmartspaceManager
 import android.app.smartspace.SmartspaceSession
 import android.app.smartspace.SmartspaceTarget
 import android.content.BroadcastReceiver
+import android.content.ContentProvider
 import android.content.ContentResolver
 import android.content.Context
 import android.content.Intent
@@ -700,10 +702,13 @@
             Log.d(TAG, "adding track for $userId from browser: $desc")
         }
 
+        val currentEntry = mediaEntries.get(packageName)
+        val appUid = currentEntry?.appUid ?: Process.INVALID_UID
+
         // Album art
         var artworkBitmap = desc.iconBitmap
         if (artworkBitmap == null && desc.iconUri != null) {
-            artworkBitmap = loadBitmapFromUri(desc.iconUri!!)
+            artworkBitmap = loadBitmapFromUriForUser(desc.iconUri!!, userId, appUid, packageName)
         }
         val artworkIcon =
             if (artworkBitmap != null) {
@@ -712,9 +717,7 @@
                 null
             }
 
-        val currentEntry = mediaEntries.get(packageName)
         val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
-        val appUid = currentEntry?.appUid ?: Process.INVALID_UID
         val isExplicit =
             desc.extras?.getLong(MediaConstants.METADATA_KEY_IS_EXPLICIT) ==
                 MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
@@ -1261,6 +1264,30 @@
             false
         }
     }
+
+    /** Returns a bitmap if the user can access the given URI, else null */
+    private fun loadBitmapFromUriForUser(
+        uri: Uri,
+        userId: Int,
+        appUid: Int,
+        packageName: String,
+    ): Bitmap? {
+        try {
+            val ugm = UriGrantsManager.getService()
+            ugm.checkGrantUriPermission_ignoreNonSystem(
+                appUid,
+                packageName,
+                ContentProvider.getUriWithoutUserId(uri),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                ContentProvider.getUserIdFromUri(uri, userId)
+            )
+            return loadBitmapFromUri(uri)
+        } catch (e: SecurityException) {
+            Log.e(TAG, "Failed to get URI permission: $e")
+        }
+        return null
+    }
+
     /**
      * Load a bitmap from a URI
      *
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 2883210..83a6e58 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -35,7 +35,7 @@
 import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.settings.SecureSettings
 import javax.inject.Inject
 import javax.inject.Named
@@ -55,6 +55,7 @@
     private val secureSettings: SecureSettings,
     @Main private val handler: Handler,
     configurationController: ConfigurationController,
+    private val splitShadeStateController: SplitShadeStateController
 ) {
 
     init {
@@ -108,7 +109,7 @@
     }
 
     private fun updateResources() {
-        useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+        useSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index c1c757e..0b30e59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -52,7 +52,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.traceSection
@@ -101,6 +101,7 @@
     panelEventsEvents: ShadeStateEvents,
     private val secureSettings: SecureSettings,
     @Main private val handler: Handler,
+    private val splitShadeStateController: SplitShadeStateController
 ) {
 
     /** Track the media player setting status on lock screen. */
@@ -568,7 +569,7 @@
             context.resources.getDimensionPixelSize(
                 R.dimen.lockscreen_shade_media_transition_distance
             )
-        inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+        inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 11538fa..33d9cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -24,7 +24,6 @@
 import com.android.launcher3.icons.IconFactory
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
 import com.android.systemui.media.MediaProjectionPermissionActivity
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -118,29 +117,8 @@
         @Provides
         @MediaProjectionAppSelector
         @MediaProjectionAppSelectorScope
-        fun provideCallerPackageName(activity: MediaProjectionAppSelectorActivity): String? =
-            activity.callingPackage
-
-        @Provides
-        @MediaProjectionAppSelector
-        @MediaProjectionAppSelectorScope
-        fun bindConfigurationController(
-            activity: MediaProjectionAppSelectorActivity
-        ): ConfigurationController = ConfigurationControllerImpl(activity)
-
-        @Provides
-        @HostUserHandle
-        @MediaProjectionAppSelectorScope
-        fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
-            val extras =
-                activity.intent.extras
-                    ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
-            return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
-                ?: error(
-                    "MediaProjectionAppSelectorActivity should be provided with " +
-                        "$EXTRA_HOST_APP_USER_HANDLE extra"
-                )
-        }
+        fun bindConfigurationController(context: Context): ConfigurationController =
+            ConfigurationControllerImpl(context)
 
         @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
 
@@ -161,7 +139,8 @@
     interface Factory {
         /** Create a factory to inject the activity into the graph */
         fun create(
-            @BindsInstance activity: MediaProjectionAppSelectorActivity,
+            @BindsInstance @HostUserHandle hostUserHandle: UserHandle,
+            @BindsInstance @MediaProjectionAppSelector callingPackage: String?,
             @BindsInstance view: MediaProjectionAppSelectorView,
             @BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
         ): MediaProjectionAppSelectorComponent
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 5d732fb..cbb7e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.mediaprojection.appselector.view
 
 import android.app.ActivityOptions
+import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
 import android.app.IActivityTaskManager
 import android.graphics.Rect
 import android.os.Binder
@@ -129,6 +130,9 @@
                 view.width,
                 view.height
             )
+        activityOptions.setPendingIntentBackgroundActivityStartMode(
+            MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+        )
         activityOptions.launchCookie = launchCookie
 
         activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle())
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 9b9d561..0c77054 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -28,7 +28,6 @@
 import android.view.WindowManager
 import androidx.core.content.getSystemService
 import androidx.core.content.res.use
-import com.android.internal.R as AndroidR
 import com.android.systemui.R
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.shared.recents.model.ThumbnailData
@@ -147,25 +146,14 @@
         previewRect.set(0, 0, thumbnailData.thumbnail.width, thumbnailData.thumbnail.height)
 
         val currentRotation: Int = display.rotation
-        val displayWidthPx = windowMetrics.bounds.width()
-        val displayHeightPx = windowMetrics.bounds.height()
         val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
         val isLargeScreen = isLargeScreen(context)
-        val taskbarSize =
-            if (isLargeScreen) {
-                resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
-            } else {
-                0
-            }
 
         previewPositionHelper.updateThumbnailMatrix(
             previewRect,
             thumbnailData,
             measuredWidth,
             measuredHeight,
-            displayWidthPx,
-            displayHeightPx,
-            taskbarSize,
             isLargeScreen,
             currentRotation,
             isRtl
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index c6f73ef..d7e062f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -233,6 +233,9 @@
                 Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
                 false, mAssistContentObserver, UserHandle.USER_ALL);
         mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Secure.SEARCH_LONG_PRESS_HOME_ENABLED),
+                false, mAssistContentObserver, UserHandle.USER_ALL);
+        mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
                 false, mAssistContentObserver, UserHandle.USER_ALL);
 
@@ -422,11 +425,17 @@
     private void updateAssistantAvailability() {
         boolean assistantAvailableForUser = mAssistManagerLazy.get()
                 .getAssistInfoForUser(mUserTracker.getUserId()) != null;
-        boolean longPressDefault = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+
+        boolean overrideLongPressHome = mAssistManagerLazy.get()
+                .shouldOverrideAssist(AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+        boolean longPressDefault = mContext.getResources().getBoolean(overrideLongPressHome
+                ? com.android.internal.R.bool.config_searchLongPressHomeEnabledDefault
+                : com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
         mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
-                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+                overrideLongPressHome ? Secure.SEARCH_LONG_PRESS_HOME_ENABLED
+                        : Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
                 mUserTracker.getUserId()) != 0;
+
         boolean gestureDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
         mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
@@ -455,6 +464,7 @@
     @Override
     public void setAssistantOverridesRequested(int[] invocationTypes) {
         mAssistManagerLazy.get().setAssistantOverridesRequested(invocationTypes);
+        updateAssistantAvailability();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 5a42028..a601d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -16,483 +16,58 @@
 
 package com.android.systemui.navigationbar;
 
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.hardware.display.DisplayManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.WindowManagerGlobal;
-
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.back.BackAnimation;
-import com.android.wm.shell.pip.Pip;
-
-import java.io.PrintWriter;
-import java.util.Optional;
-
-import javax.inject.Inject;
+import com.android.systemui.statusbar.phone.BarTransitions;
 
 /** A controller to handle navigation bars. */
-@SysUISingleton
-public class NavigationBarController implements
-        ConfigurationController.ConfigurationListener,
-        NavigationModeController.ModeChangedListener,
-        Dumpable {
-
-    private static final String TAG = NavigationBarController.class.getSimpleName();
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
-    private FeatureFlags mFeatureFlags;
-    private final SecureSettings mSecureSettings;
-    private final DisplayTracker mDisplayTracker;
-    private final DisplayManager mDisplayManager;
-    private final TaskbarDelegate mTaskbarDelegate;
-    private final NavBarHelper mNavBarHelper;
-    private int mNavMode;
-    @VisibleForTesting boolean mIsLargeScreen;
-
-    /** A displayId - nav bar maps. */
-    @VisibleForTesting
-    SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
-
-    // Tracks config changes that will actually recreate the nav bar
-    private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
-            ActivityInfo.CONFIG_FONT_SCALE
-                    | ActivityInfo.CONFIG_UI_MODE);
-
-    @Inject
-    public NavigationBarController(Context context,
-            OverviewProxyService overviewProxyService,
-            NavigationModeController navigationModeController,
-            SysUiState sysUiFlagsContainer,
-            CommandQueue commandQueue,
-            @Main Handler mainHandler,
-            ConfigurationController configurationController,
-            NavBarHelper navBarHelper,
-            TaskbarDelegate taskbarDelegate,
-            NavigationBarComponent.Factory navigationBarComponentFactory,
-            DumpManager dumpManager,
-            AutoHideController autoHideController,
-            LightBarController lightBarController,
-            TaskStackChangeListeners taskStackChangeListeners,
-            Optional<Pip> pipOptional,
-            Optional<BackAnimation> backAnimation,
-            FeatureFlags featureFlags,
-            SecureSettings secureSettings,
-            DisplayTracker displayTracker) {
-        mContext = context;
-        mHandler = mainHandler;
-        mNavigationBarComponentFactory = navigationBarComponentFactory;
-        mFeatureFlags = featureFlags;
-        mSecureSettings = secureSettings;
-        mDisplayTracker = displayTracker;
-        mDisplayManager = mContext.getSystemService(DisplayManager.class);
-        commandQueue.addCallback(mCommandQueueCallbacks);
-        configurationController.addCallback(this);
-        mConfigChanges.applyNewConfig(mContext.getResources());
-        mNavMode = navigationModeController.addListener(this);
-        mNavBarHelper = navBarHelper;
-        mTaskbarDelegate = taskbarDelegate;
-        mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
-                navBarHelper, navigationModeController, sysUiFlagsContainer,
-                dumpManager, autoHideController, lightBarController, pipOptional,
-                backAnimation.orElse(null), taskStackChangeListeners);
-        mIsLargeScreen = isLargeScreen(mContext);
-        dumpManager.registerDumpable(this);
-    }
-
-    @Override
-    public void onConfigChanged(Configuration newConfig) {
-        boolean isOldConfigLargeScreen = mIsLargeScreen;
-        mIsLargeScreen = isLargeScreen(mContext);
-        boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
-        boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
-        // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
-        Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
-                + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
-                + " willApplyConfigToNavbars=" + willApplyConfig
-                + " navBarCount=" + mNavigationBars.size());
-        if (mTaskbarDelegate.isInitialized()) {
-            mTaskbarDelegate.onConfigurationChanged(newConfig);
-        }
-        // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
-        if (largeScreenChanged && updateNavbarForTaskbar()) {
-            return;
-        }
-
-        if (willApplyConfig) {
-            for (int i = 0; i < mNavigationBars.size(); i++) {
-                recreateNavigationBar(mNavigationBars.keyAt(i));
-            }
-        } else {
-            for (int i = 0; i < mNavigationBars.size(); i++) {
-                mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
-            }
-        }
-    }
-
-    @Override
-    public void onNavigationModeChanged(int mode) {
-        if (mNavMode == mode) {
-            return;
-        }
-        final int oldMode = mNavMode;
-        mNavMode = mode;
-        updateAccessibilityButtonModeIfNeeded();
-
-        mHandler.post(() -> {
-            // create/destroy nav bar based on nav mode only in unfolded state
-            if (oldMode != mNavMode) {
-                updateNavbarForTaskbar();
-            }
-            for (int i = 0; i < mNavigationBars.size(); i++) {
-                NavigationBar navBar = mNavigationBars.valueAt(i);
-                if (navBar == null) {
-                    continue;
-                }
-                navBar.getView().updateStates();
-            }
-        });
-    }
-
-    private void updateAccessibilityButtonModeIfNeeded() {
-        final int mode = mSecureSettings.getIntForUser(
-                Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
-                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
-        // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
-        // mode, so we don't need to update it.
-        if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
-            return;
-        }
-
-        // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
-        // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
-        if (QuickStepContract.isGesturalMode(mNavMode)
-                && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
-            mSecureSettings.putIntForUser(
-                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
-                    UserHandle.USER_CURRENT);
-            // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
-            // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
-        } else if (!QuickStepContract.isGesturalMode(mNavMode)
-                && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
-            mSecureSettings.putIntForUser(
-                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
-                    ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-        }
-    }
-
-    private boolean shouldCreateNavBarAndTaskBar(int displayId) {
-        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
-        try {
-            return wms.hasNavigationBar(displayId);
-        } catch (RemoteException e) {
-            // Cannot get wms, just return false with warning message.
-            Log.w(TAG, "Cannot get WindowManager.");
-            return false;
-        }
-    }
-
-    /** @see #initializeTaskbarIfNecessary() */
-    private boolean updateNavbarForTaskbar() {
-        boolean taskbarShown = initializeTaskbarIfNecessary();
-        if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
-            createNavigationBar(mContext.getDisplay(), null, null);
-        }
-        return taskbarShown;
-    }
-
-    /** @return {@code true} if taskbar is enabled, false otherwise */
-    private boolean initializeTaskbarIfNecessary() {
-        // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
-        boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
-                Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
-
-        if (taskbarEnabled) {
-            Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
-            final int displayId = mContext.getDisplayId();
-            // Hint to NavBarHelper if we are replacing an existing bar to skip extra work
-            mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
-            // Remove navigation bar when taskbar is showing
-            removeNavigationBar(displayId);
-            mTaskbarDelegate.init(displayId);
-            mNavBarHelper.setTogglingNavbarTaskbar(false);
-            Trace.endSection();
-
-        } else {
-            mTaskbarDelegate.destroy();
-        }
-        return taskbarEnabled;
-    }
-
-    private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
-        @Override
-        public void onDisplayRemoved(int displayId) {
-            removeNavigationBar(displayId);
-        }
-
-        @Override
-        public void onDisplayReady(int displayId) {
-            Display display = mDisplayManager.getDisplay(displayId);
-            mIsLargeScreen = isLargeScreen(mContext);
-            createNavigationBar(display, null /* savedState */, null /* result */);
-        }
-
-        @Override
-        public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
-            final NavigationBar navigationBar = getNavigationBar(displayId);
-            if (navigationBar != null) {
-                navigationBar.setNavigationBarLumaSamplingEnabled(enable);
-            }
-        }
-
-        @Override
-        public void showPinningEnterExitToast(boolean entering) {
-            int displayId = mContext.getDisplayId();
-            final NavigationBarView navBarView = getNavigationBarView(displayId);
-            if (navBarView != null) {
-                navBarView.showPinningEnterExitToast(entering);
-            } else if (displayId == mDisplayTracker.getDefaultDisplayId()
-                    && mTaskbarDelegate.isInitialized()) {
-                mTaskbarDelegate.showPinningEnterExitToast(entering);
-            }
-        }
-
-        @Override
-        public void showPinningEscapeToast() {
-            int displayId = mContext.getDisplayId();
-            final NavigationBarView navBarView = getNavigationBarView(displayId);
-            if (navBarView != null) {
-                navBarView.showPinningEscapeToast();
-            } else if (displayId == mDisplayTracker.getDefaultDisplayId()
-                    && mTaskbarDelegate.isInitialized()) {
-                mTaskbarDelegate.showPinningEscapeToast();
-            }
-        }
-    };
-
-    /**
-     * Recreates the navigation bar for the given display.
-     */
-    private void recreateNavigationBar(int displayId) {
-        // TODO: Improve this flow so that we don't need to create a new nav bar but just
-        //       the view
-        Bundle savedState = new Bundle();
-        NavigationBar bar = mNavigationBars.get(displayId);
-        if (bar != null) {
-            bar.onSaveInstanceState(savedState);
-        }
-        removeNavigationBar(displayId);
-        createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */);
-    }
-
-    // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
-    // CarStatusBar because they have their own nav bar. Think about a better way for it.
+public interface NavigationBarController {
     /**
      * Creates navigation bars when car/status bar initializes.
+     * <p>
+     * TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
+     * CarStatusBar because they have their own nav bar. Think about a better way for it.
      *
      * @param includeDefaultDisplay {@code true} to create navigation bar on default display.
      */
-    public void createNavigationBars(final boolean includeDefaultDisplay,
-            RegisterStatusBarResult result) {
-        updateAccessibilityButtonModeIfNeeded();
+    void createNavigationBars(
+            boolean includeDefaultDisplay,
+            RegisterStatusBarResult result);
 
-        // Don't need to create nav bar on the default display if we initialize TaskBar.
-        final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
-                && !initializeTaskbarIfNecessary();
-        Display[] displays = mDisplayTracker.getAllDisplays();
-        for (Display display : displays) {
-            if (shouldCreateDefaultNavbar
-                    || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
-                createNavigationBar(display, null /* savedState */, result);
-            }
-        }
-    }
-
-    /**
-     * Adds a navigation bar on default display or an external display if the display supports
-     * system decorations.
-     *
-     * @param display the display to add navigation bar on.
-     */
-    @VisibleForTesting
-    void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
-        if (display == null) {
-            return;
-        }
-
-        final int displayId = display.getDisplayId();
-        final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
-
-        if (!shouldCreateNavBarAndTaskBar(displayId)) {
-            return;
-        }
-
-        // We may show TaskBar on the default display for large screen device. Don't need to create
-        // navigation bar for this case.
-        if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
-            return;
-        }
-
-        final Context context = isOnDefaultDisplay
-                ? mContext
-                : mContext.createDisplayContext(display);
-        NavigationBarComponent component = mNavigationBarComponentFactory.create(
-                context, savedState);
-        NavigationBar navBar = component.getNavigationBar();
-        navBar.init();
-        mNavigationBars.put(displayId, navBar);
-
-        navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-            @Override
-            public void onViewAttachedToWindow(View v) {
-                if (result != null) {
-                    navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
-                            result.mImeWindowVis, result.mImeBackDisposition,
-                            result.mShowImeSwitcher);
-                }
-            }
-
-            @Override
-            public void onViewDetachedFromWindow(View v) {
-                v.removeOnAttachStateChangeListener(this);
-            }
-        });
-    }
-
-    void removeNavigationBar(int displayId) {
-        NavigationBar navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            navBar.destroyView();
-            mNavigationBars.remove(displayId);
-        }
-    }
+    /** Removes the navigation bar for the given display ID. */
+    void removeNavigationBar(int displayId);
 
     /** @see NavigationBar#checkNavBarModes() */
-    public void checkNavBarModes(int displayId) {
-        NavigationBar navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            navBar.checkNavBarModes();
-        }
-    }
+    void checkNavBarModes(int displayId);
 
     /** @see NavigationBar#finishBarAnimations() */
-    public void finishBarAnimations(int displayId) {
-        NavigationBar navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            navBar.finishBarAnimations();
-        }
-    }
+    void finishBarAnimations(int displayId);
 
     /** @see NavigationBar#touchAutoDim() */
-    public void touchAutoDim(int displayId) {
-        NavigationBar navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            navBar.touchAutoDim();
-        }
-    }
+    void touchAutoDim(int displayId);
 
     /** @see NavigationBar#transitionTo(int, boolean) */
-    public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
-        NavigationBar navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            navBar.transitionTo(barMode, animate);
-        }
-    }
+    void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate);
 
     /** @see NavigationBar#disableAnimationsDuringHide(long) */
-    public void disableAnimationsDuringHide(int displayId, long delay) {
-        NavigationBar navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            navBar.disableAnimationsDuringHide(delay);
-        }
-    }
+    void disableAnimationsDuringHide(int displayId, long delay);
 
     /** @return {@link NavigationBarView} on the default display. */
-    public @Nullable NavigationBarView getDefaultNavigationBarView() {
-        return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
-    }
+    @Nullable
+    NavigationBarView getDefaultNavigationBarView();
 
     /**
      * @param displayId the ID of display which Navigation bar is on
      * @return {@link NavigationBarView} on the display with {@code displayId}.
      *         {@code null} if no navigation bar on that display.
      */
-    public @Nullable NavigationBarView getNavigationBarView(int displayId) {
-        NavigationBar navBar = getNavigationBar(displayId);
-        return (navBar == null) ? null : navBar.getView();
-    }
+    @Nullable
+    NavigationBarView getNavigationBarView(int displayId);
 
-    private @Nullable NavigationBar getNavigationBar(int displayId) {
-        return mNavigationBars.get(displayId);
-    }
-
-    public boolean isOverviewEnabled(int displayId) {
-        final NavigationBarView navBarView = getNavigationBarView(displayId);
-        if (navBarView != null) {
-            return navBarView.isOverviewEnabled();
-        } else {
-            return mTaskbarDelegate.isOverviewEnabled();
-        }
-    }
+    boolean isOverviewEnabled(int displayId);
 
     /** @return {@link NavigationBar} on the default display. */
     @Nullable
-    public NavigationBar getDefaultNavigationBar() {
-        return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
-    }
-
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("mIsLargeScreen=" + mIsLargeScreen);
-        pw.println("mNavMode=" + mNavMode);
-        for (int i = 0; i < mNavigationBars.size(); i++) {
-            if (i > 0) {
-                pw.println();
-            }
-            mNavigationBars.valueAt(i).dump(pw);
-        }
-    }
+    NavigationBar getDefaultNavigationBar();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
new file mode 100644
index 0000000..e73b078
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -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 com.android.systemui.navigationbar
+
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.BarTransitions
+import javax.inject.Inject
+
+/** A no-op version of [NavigationBarController] for variants like Arc and TV. */
+@SysUISingleton
+class NavigationBarControllerEmptyImpl @Inject constructor() : NavigationBarController {
+    override fun createNavigationBars(
+        includeDefaultDisplay: Boolean,
+        result: RegisterStatusBarResult?,
+    ) {}
+    override fun removeNavigationBar(displayId: Int) {}
+    override fun checkNavBarModes(displayId: Int) {}
+    override fun finishBarAnimations(displayId: Int) {}
+    override fun touchAutoDim(displayId: Int) {}
+    override fun transitionTo(
+        displayId: Int,
+        @BarTransitions.TransitionMode barMode: Int,
+        animate: Boolean,
+    ) {}
+    override fun disableAnimationsDuringHide(displayId: Int, delay: Long) {}
+    override fun getDefaultNavigationBarView(): NavigationBarView? = null
+    override fun getNavigationBarView(displayId: Int): NavigationBarView? = null
+    override fun isOverviewEnabled(displayId: Int) = false
+    override fun getDefaultNavigationBar(): NavigationBar? = null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
new file mode 100644
index 0000000..564e984
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.pip.Pip;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+@SysUISingleton
+public class NavigationBarControllerImpl implements
+        ConfigurationController.ConfigurationListener,
+        NavigationModeController.ModeChangedListener,
+        Dumpable, NavigationBarController {
+
+    private static final String TAG = NavigationBarControllerImpl.class.getSimpleName();
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
+    private FeatureFlags mFeatureFlags;
+    private final SecureSettings mSecureSettings;
+    private final DisplayTracker mDisplayTracker;
+    private final DisplayManager mDisplayManager;
+    private final TaskbarDelegate mTaskbarDelegate;
+    private final NavBarHelper mNavBarHelper;
+    private int mNavMode;
+    @VisibleForTesting boolean mIsLargeScreen;
+
+    /** A displayId - nav bar maps. */
+    @VisibleForTesting
+    SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
+
+    // Tracks config changes that will actually recreate the nav bar
+    private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
+            ActivityInfo.CONFIG_FONT_SCALE
+                    | ActivityInfo.CONFIG_UI_MODE);
+
+    @Inject
+    public NavigationBarControllerImpl(Context context,
+            OverviewProxyService overviewProxyService,
+            NavigationModeController navigationModeController,
+            SysUiState sysUiFlagsContainer,
+            CommandQueue commandQueue,
+            @Main Handler mainHandler,
+            ConfigurationController configurationController,
+            NavBarHelper navBarHelper,
+            TaskbarDelegate taskbarDelegate,
+            NavigationBarComponent.Factory navigationBarComponentFactory,
+            DumpManager dumpManager,
+            AutoHideController autoHideController,
+            LightBarController lightBarController,
+            TaskStackChangeListeners taskStackChangeListeners,
+            Optional<Pip> pipOptional,
+            Optional<BackAnimation> backAnimation,
+            FeatureFlags featureFlags,
+            SecureSettings secureSettings,
+            DisplayTracker displayTracker) {
+        mContext = context;
+        mHandler = mainHandler;
+        mNavigationBarComponentFactory = navigationBarComponentFactory;
+        mFeatureFlags = featureFlags;
+        mSecureSettings = secureSettings;
+        mDisplayTracker = displayTracker;
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        commandQueue.addCallback(mCommandQueueCallbacks);
+        configurationController.addCallback(this);
+        mConfigChanges.applyNewConfig(mContext.getResources());
+        mNavMode = navigationModeController.addListener(this);
+        mNavBarHelper = navBarHelper;
+        mTaskbarDelegate = taskbarDelegate;
+        mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
+                navBarHelper, navigationModeController, sysUiFlagsContainer,
+                dumpManager, autoHideController, lightBarController, pipOptional,
+                backAnimation.orElse(null), taskStackChangeListeners);
+        mIsLargeScreen = isLargeScreen(mContext);
+        dumpManager.registerDumpable(this);
+    }
+
+    @Override
+    public void onConfigChanged(Configuration newConfig) {
+        boolean isOldConfigLargeScreen = mIsLargeScreen;
+        mIsLargeScreen = isLargeScreen(mContext);
+        boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
+        boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
+        // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+        Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+                + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
+                + " willApplyConfigToNavbars=" + willApplyConfig
+                + " navBarCount=" + mNavigationBars.size());
+        if (mTaskbarDelegate.isInitialized()) {
+            mTaskbarDelegate.onConfigurationChanged(newConfig);
+        }
+        // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
+        if (largeScreenChanged && updateNavbarForTaskbar()) {
+            return;
+        }
+
+        if (willApplyConfig) {
+            for (int i = 0; i < mNavigationBars.size(); i++) {
+                recreateNavigationBar(mNavigationBars.keyAt(i));
+            }
+        } else {
+            for (int i = 0; i < mNavigationBars.size(); i++) {
+                mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
+            }
+        }
+    }
+
+    @Override
+    public void onNavigationModeChanged(int mode) {
+        if (mNavMode == mode) {
+            return;
+        }
+        final int oldMode = mNavMode;
+        mNavMode = mode;
+        updateAccessibilityButtonModeIfNeeded();
+
+        mHandler.post(() -> {
+            // create/destroy nav bar based on nav mode only in unfolded state
+            if (oldMode != mNavMode) {
+                updateNavbarForTaskbar();
+            }
+            for (int i = 0; i < mNavigationBars.size(); i++) {
+                NavigationBar navBar = mNavigationBars.valueAt(i);
+                if (navBar == null) {
+                    continue;
+                }
+                navBar.getView().updateStates();
+            }
+        });
+    }
+
+    private void updateAccessibilityButtonModeIfNeeded() {
+        final int mode = mSecureSettings.getIntForUser(
+                Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+        // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+        // mode, so we don't need to update it.
+        if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+            return;
+        }
+
+        // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+        // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+        if (QuickStepContract.isGesturalMode(mNavMode)
+                && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+            mSecureSettings.putIntForUser(
+                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+                    UserHandle.USER_CURRENT);
+            // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+            // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+        } else if (!QuickStepContract.isGesturalMode(mNavMode)
+                && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+            mSecureSettings.putIntForUser(
+                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+                    ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+        }
+    }
+
+    private boolean shouldCreateNavBarAndTaskBar(int displayId) {
+        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+        try {
+            return wms.hasNavigationBar(displayId);
+        } catch (RemoteException e) {
+            // Cannot get wms, just return false with warning message.
+            Log.w(TAG, "Cannot get WindowManager.");
+            return false;
+        }
+    }
+
+    /** @see #initializeTaskbarIfNecessary() */
+    private boolean updateNavbarForTaskbar() {
+        boolean taskbarShown = initializeTaskbarIfNecessary();
+        if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
+            createNavigationBar(mContext.getDisplay(), null, null);
+        }
+        return taskbarShown;
+    }
+
+    /** @return {@code true} if taskbar is enabled, false otherwise */
+    private boolean initializeTaskbarIfNecessary() {
+        // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
+        boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
+                Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
+
+        if (taskbarEnabled) {
+            Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
+            final int displayId = mContext.getDisplayId();
+            // Hint to NavBarHelper if we are replacing an existing bar to skip extra work
+            mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
+            // Remove navigation bar when taskbar is showing
+            removeNavigationBar(displayId);
+            mTaskbarDelegate.init(displayId);
+            mNavBarHelper.setTogglingNavbarTaskbar(false);
+            Trace.endSection();
+
+        } else {
+            mTaskbarDelegate.destroy();
+        }
+        return taskbarEnabled;
+    }
+
+    private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            removeNavigationBar(displayId);
+        }
+
+        @Override
+        public void onDisplayReady(int displayId) {
+            Display display = mDisplayManager.getDisplay(displayId);
+            mIsLargeScreen = isLargeScreen(mContext);
+            createNavigationBar(display, null /* savedState */, null /* result */);
+        }
+
+        @Override
+        public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+            final NavigationBar navigationBar = getNavigationBar(displayId);
+            if (navigationBar != null) {
+                navigationBar.setNavigationBarLumaSamplingEnabled(enable);
+            }
+        }
+
+        @Override
+        public void showPinningEnterExitToast(boolean entering) {
+            int displayId = mContext.getDisplayId();
+            final NavigationBarView navBarView = getNavigationBarView(displayId);
+            if (navBarView != null) {
+                navBarView.showPinningEnterExitToast(entering);
+            } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+                    && mTaskbarDelegate.isInitialized()) {
+                mTaskbarDelegate.showPinningEnterExitToast(entering);
+            }
+        }
+
+        @Override
+        public void showPinningEscapeToast() {
+            int displayId = mContext.getDisplayId();
+            final NavigationBarView navBarView = getNavigationBarView(displayId);
+            if (navBarView != null) {
+                navBarView.showPinningEscapeToast();
+            } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+                    && mTaskbarDelegate.isInitialized()) {
+                mTaskbarDelegate.showPinningEscapeToast();
+            }
+        }
+    };
+
+    /**
+     * Recreates the navigation bar for the given display.
+     */
+    private void recreateNavigationBar(int displayId) {
+        // TODO: Improve this flow so that we don't need to create a new nav bar but just
+        //       the view
+        Bundle savedState = new Bundle();
+        NavigationBar bar = mNavigationBars.get(displayId);
+        if (bar != null) {
+            bar.onSaveInstanceState(savedState);
+        }
+        removeNavigationBar(displayId);
+        createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */);
+    }
+
+    @Override
+    public void createNavigationBars(final boolean includeDefaultDisplay,
+            RegisterStatusBarResult result) {
+        updateAccessibilityButtonModeIfNeeded();
+
+        // Don't need to create nav bar on the default display if we initialize TaskBar.
+        final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
+                && !initializeTaskbarIfNecessary();
+        Display[] displays = mDisplayTracker.getAllDisplays();
+        for (Display display : displays) {
+            if (shouldCreateDefaultNavbar
+                    || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
+                createNavigationBar(display, null /* savedState */, result);
+            }
+        }
+    }
+
+    /**
+     * Adds a navigation bar on default display or an external display if the display supports
+     * system decorations.
+     *
+     * @param display the display to add navigation bar on.
+     */
+    @VisibleForTesting
+    void createNavigationBar(Display display, Bundle savedState,
+            RegisterStatusBarResult result) {
+        if (display == null) {
+            return;
+        }
+
+        final int displayId = display.getDisplayId();
+        final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
+
+        if (!shouldCreateNavBarAndTaskBar(displayId)) {
+            return;
+        }
+
+        // We may show TaskBar on the default display for large screen device. Don't need to create
+        // navigation bar for this case.
+        if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
+            return;
+        }
+
+        final Context context = isOnDefaultDisplay
+                ? mContext
+                : mContext.createDisplayContext(display);
+        NavigationBarComponent component = mNavigationBarComponentFactory.create(
+                context, savedState);
+        NavigationBar navBar = component.getNavigationBar();
+        navBar.init();
+        mNavigationBars.put(displayId, navBar);
+
+        navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                if (result != null) {
+                    navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
+                            result.mImeWindowVis, result.mImeBackDisposition,
+                            result.mShowImeSwitcher);
+                }
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                v.removeOnAttachStateChangeListener(this);
+            }
+        });
+    }
+
+    @Override
+    public void removeNavigationBar(int displayId) {
+        NavigationBar navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.destroyView();
+            mNavigationBars.remove(displayId);
+        }
+    }
+
+    @Override
+    public void checkNavBarModes(int displayId) {
+        NavigationBar navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.checkNavBarModes();
+        }
+    }
+
+    @Override
+    public void finishBarAnimations(int displayId) {
+        NavigationBar navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.finishBarAnimations();
+        }
+    }
+
+    @Override
+    public void touchAutoDim(int displayId) {
+        NavigationBar navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.touchAutoDim();
+        }
+    }
+
+    @Override
+    public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
+        NavigationBar navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.transitionTo(barMode, animate);
+        }
+    }
+
+    @Override
+    public void disableAnimationsDuringHide(int displayId, long delay) {
+        NavigationBar navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.disableAnimationsDuringHide(delay);
+        }
+    }
+
+    @Override
+    public @Nullable NavigationBarView getDefaultNavigationBarView() {
+        return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
+    }
+
+    @Override
+    public @Nullable NavigationBarView getNavigationBarView(int displayId) {
+        NavigationBar navBar = getNavigationBar(displayId);
+        return (navBar == null) ? null : navBar.getView();
+    }
+
+    private @Nullable NavigationBar getNavigationBar(int displayId) {
+        return mNavigationBars.get(displayId);
+    }
+
+    @Override
+    public boolean isOverviewEnabled(int displayId) {
+        final NavigationBarView navBarView = getNavigationBarView(displayId);
+        if (navBarView != null) {
+            return navBarView.isOverviewEnabled();
+        } else {
+            return mTaskbarDelegate.isOverviewEnabled();
+        }
+    }
+
+    @Override
+    @Nullable
+    public NavigationBar getDefaultNavigationBar() {
+        return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
+    }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("mIsLargeScreen=" + mIsLargeScreen);
+        pw.println("mNavMode=" + mNavMode);
+        for (int i = 0; i < mNavigationBars.size(); i++) {
+            if (i > 0) {
+                pw.println();
+            }
+            mNavigationBars.valueAt(i).dump(pw);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt
new file mode 100644
index 0000000..448f280
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerModule.kt
@@ -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 com.android.systemui.navigationbar
+
+import dagger.Binds
+import dagger.Module
+
+/** A module providing an instance of [NavigationBarController]. */
+@Module
+abstract class NavigationBarControllerModule {
+    @Binds
+    abstract fun navigationBarController(impl: NavigationBarControllerImpl): NavigationBarController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 146b5f5..79e7b71 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -359,6 +359,7 @@
 
     public void setBackgroundExecutor(Executor bgExecutor) {
         mBgExecutor = bgExecutor;
+        mRotationButtonController.setBgExecutor(bgExecutor);
     }
 
     public void setDisplayTracker(DisplayTracker displayTracker) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt
new file mode 100644
index 0000000..b59912a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NoopNavigationBarControllerModule.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.navigationbar
+
+import dagger.Binds
+import dagger.Module
+
+/** A module providing a no-op instance of [NavigationBarController]. */
+@Module
+abstract class NoopNavigationBarControllerModule {
+    @Binds
+    abstract fun navigationBarController(
+        impl: NavigationBarControllerEmptyImpl
+    ): NavigationBarController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 0842fe0..ea8eb36 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -321,7 +321,7 @@
         // When switched to a secondary user, the sysUI is still running in the main user, we will
         // need to update the shortcut in the secondary user.
         if (user == getCurrentRunningUser()) {
-            updateNoteTaskAsUserInternal(user)
+            launchUpdateNoteTaskAsUser(user)
         } else {
             // TODO(b/278729185): Replace fire and forget service with a bounded service.
             val intent = NoteTaskControllerUpdateService.createIntent(context)
@@ -330,23 +330,25 @@
     }
 
     @InternalNoteTaskApi
-    fun updateNoteTaskAsUserInternal(user: UserHandle) {
-        if (!userManager.isUserUnlocked(user)) {
-            debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
-            return
-        }
+    fun launchUpdateNoteTaskAsUser(user: UserHandle) {
+        applicationScope.launch {
+            if (!userManager.isUserUnlocked(user)) {
+                debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
+                return@launch
+            }
 
-        val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
-        val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
+            val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
+            val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
 
-        setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
+            setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
 
-        if (hasNotesRoleHolder) {
-            shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
-            val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
-            shortcutManager.updateShortcuts(listOf(updatedShortcut))
-        } else {
-            shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
+            if (hasNotesRoleHolder) {
+                shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
+                val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
+                shortcutManager.updateShortcuts(listOf(updatedShortcut))
+            } else {
+                shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
index 3e352af..486fde1 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
@@ -44,7 +44,7 @@
     override fun onCreate() {
         super.onCreate()
         // TODO(b/278729185): Replace fire and forget service with a bounded service.
-        controller.updateNoteTaskAsUserInternal(user)
+        controller.launchUpdateNoteTaskAsUser(user)
         stopSelf()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index fe1034a..338d3ed 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -23,6 +23,7 @@
 import android.view.KeyEvent
 import android.view.KeyEvent.KEYCODE_N
 import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
+import android.view.ViewConfiguration
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.dagger.qualifiers.Background
@@ -65,12 +66,6 @@
      * [NoteTaskController], ensure custom actions can be triggered (i.e., keyboard shortcut).
      */
     private fun initializeHandleSystemKey() {
-        val callbacks =
-            object : CommandQueue.Callbacks {
-                override fun handleSystemKey(key: KeyEvent) {
-                    key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask)
-                }
-            }
         commandQueue.addCallback(callbacks)
     }
 
@@ -134,15 +129,39 @@
                 controller.updateNoteTaskForCurrentUserAndManagedProfiles()
             }
         }
-}
 
-/**
- * Maps a [KeyEvent] to a [NoteTaskEntryPoint]. If the [KeyEvent] does not represent a
- * [NoteTaskEntryPoint], returns null.
- */
-private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
-    when {
-        keyCode == KEYCODE_STYLUS_BUTTON_TAIL -> TAIL_BUTTON
-        keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
-        else -> null
+    /**
+     * Tracks a [KeyEvent], and determines if it should trigger an action to show the note task.
+     * Returns a [NoteTaskEntryPoint] if an action should be taken, and null otherwise.
+     */
+    private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
+        when {
+            keyCode == KEYCODE_STYLUS_BUTTON_TAIL && isTailButtonNotesGesture() -> TAIL_BUTTON
+            keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
+            else -> null
+        }
+
+    private var lastStylusButtonTailUpEventTime: Long = -MULTI_PRESS_TIMEOUT
+
+    /**
+     * Perform gesture detection for the stylus tail button to make sure we only show the note task
+     * when there is a single press. Long presses and multi-presses are ignored for now.
+     */
+    private fun KeyEvent.isTailButtonNotesGesture(): Boolean {
+        if (keyCode != KEYCODE_STYLUS_BUTTON_TAIL || action != KeyEvent.ACTION_UP) {
+            return false
+        }
+
+        val isMultiPress = (downTime - lastStylusButtonTailUpEventTime) < MULTI_PRESS_TIMEOUT
+        val isLongPress = (eventTime - downTime) >= LONG_PRESS_TIMEOUT
+        lastStylusButtonTailUpEventTime = eventTime
+        // For now, trigger action immediately on UP of a single press, without waiting for
+        // the multi-press timeout to expire.
+        return !isMultiPress && !isLongPress
     }
+
+    companion object {
+        val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong()
+        val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
deleted file mode 100644
index e473dd2..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-
-import com.android.systemui.qs.tileimpl.SlashImageView;
-
-
-/**
- * Creates AlphaControlledSlashImageView instead of SlashImageView
- */
-public class AlphaControlledSignalTileView extends SignalTileView {
-    public AlphaControlledSignalTileView(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected SlashImageView createSlashImageView(Context context) {
-        return new AlphaControlledSlashImageView(context);
-    }
-
-    /**
-     * Creates AlphaControlledSlashDrawable instead of regular SlashDrawables
-     */
-    public static class AlphaControlledSlashImageView extends SlashImageView {
-        public AlphaControlledSlashImageView(Context context) {
-            super(context);
-        }
-
-        public void setFinalImageTintList(ColorStateList tint) {
-            super.setImageTintList(tint);
-            final SlashDrawable slash = getSlash();
-            if (slash != null) {
-                ((AlphaControlledSlashDrawable)slash).setFinalTintList(tint);
-            }
-        }
-
-        @Override
-        protected void ensureSlashDrawable() {
-            if (getSlash() == null) {
-                final SlashDrawable slash = new AlphaControlledSlashDrawable(getDrawable());
-                setSlash(slash);
-                slash.setAnimationEnabled(getAnimationEnabled());
-                setImageViewDrawable(slash);
-            }
-        }
-    }
-
-    /**
-     * SlashDrawable that disobeys orders to change its drawable's tint except when you tell
-     * it not to disobey. The slash still will animate its alpha.
-     */
-    public static class AlphaControlledSlashDrawable extends SlashDrawable {
-        AlphaControlledSlashDrawable(Drawable d) {
-            super(d);
-        }
-
-        @Override
-        protected void setDrawableTintList(@Nullable ColorStateList tint) {
-        }
-
-        /**
-         * Set a target tint list instead of
-         */
-        public void setFinalTintList(ColorStateList tint) {
-            super.setDrawableTintList(tint);
-        }
-    }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 856c64a..4e914a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -21,9 +21,7 @@
 import android.provider.Settings;
 
 import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.util.leak.GarbageMonitor;
 
 import java.util.ArrayList;
@@ -63,12 +61,7 @@
     void removeTiles(Collection<String> specs);
 
     List<String> getSpecs();
-    /**
-     * Create a view for a tile, iterating over all possible {@link QSFactory}.
-     *
-     * @see QSFactory#createTileView
-     */
-    QSTileView createTileView(Context themedContext, QSTile tile, boolean collapsedView);
+
     /** Create a {@link QSTile} of a {@code tileSpec} type.
      *
      * This should only be called by classes that need to create one-off instances of tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index cb87e3c..2a36fdb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.QSTileView
 import com.android.systemui.qs.external.TileServiceRequestController
 import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -176,14 +175,6 @@
         }
     }
 
-    override fun createTileView(
-        themedContext: Context?,
-        tile: QSTile?,
-        collapsedView: Boolean
-    ): QSTileView {
-        return qsTileHost.createTileView(themedContext, tile, collapsedView)
-    }
-
     override fun createTile(tileSpec: String): QSTile? {
         return qsTileHost.createTile(tileSpec)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 856a92e..9359958 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -39,6 +39,7 @@
 import com.android.systemui.settings.brightness.BrightnessSliderController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.tuner.TunerService;
 
 import javax.inject.Inject;
@@ -80,9 +81,10 @@
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSliderController.Factory brightnessSliderFactory,
             FalsingManager falsingManager,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            SplitShadeStateController splitShadeStateController) {
         super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
-                metricsLogger, uiEventLogger, qsLogger, dumpManager);
+                metricsLogger, uiEventLogger, qsLogger, dumpManager, splitShadeStateController);
         mTunerService = tunerService;
         mQsCustomizerController = qsCustomizerController;
         mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 20f0352..ef81674 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -40,10 +40,13 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileViewImpl;
-import com.android.systemui.util.LargeScreenUtils;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.animation.DisappearParameters;
 
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -53,9 +56,6 @@
 
 import javax.inject.Named;
 
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
 /**
  * Controller for QSPanel views.
  *
@@ -86,6 +86,8 @@
 
     private final QSHost.Callback mQSHostCallback = this::setTiles;
 
+    private SplitShadeStateController mSplitShadeStateController;
+
     @VisibleForTesting
     protected final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
@@ -93,8 +95,8 @@
                 public void onConfigurationChange(Configuration newConfig) {
                     final boolean previousSplitShadeState = mShouldUseSplitNotificationShade;
                     final int previousOrientation = mLastOrientation;
-                    mShouldUseSplitNotificationShade =
-                            LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+                    mShouldUseSplitNotificationShade = mSplitShadeStateController
+                            .shouldUseSplitNotificationShade(getResources());
                     mLastOrientation = newConfig.orientation;
 
                     mQSLogger.logOnConfigurationChanged(
@@ -138,7 +140,8 @@
             MetricsLogger metricsLogger,
             UiEventLogger uiEventLogger,
             QSLogger qsLogger,
-            DumpManager dumpManager
+            DumpManager dumpManager,
+            SplitShadeStateController splitShadeStateController
     ) {
         super(view);
         mHost = host;
@@ -149,8 +152,9 @@
         mUiEventLogger = uiEventLogger;
         mQSLogger = qsLogger;
         mDumpManager = dumpManager;
+        mSplitShadeStateController = splitShadeStateController;
         mShouldUseSplitNotificationShade =
-                LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+                mSplitShadeStateController.shouldUseSplitNotificationShade(getResources());
     }
 
     @Override
@@ -246,8 +250,8 @@
     }
 
     private void addTile(final QSTile tile, boolean collapsedView) {
-        final TileRecord r =
-                new TileRecord(tile, mHost.createTileView(getContext(), tile, collapsedView));
+        final QSTileViewImpl tileView = new QSTileViewImpl(getContext(), collapsedView);
+        final TileRecord r = new TileRecord(tile, tileView);
         // TODO(b/250618218): Remove the QSLogger in QSTileViewImpl once we know the root cause of
         // b/250618218.
         try {
@@ -486,7 +490,6 @@
 
         public QSTile tile;
         public com.android.systemui.plugins.qs.QSTileView tileView;
-        public boolean scanState;
         @Nullable
         public QSTile.Callback callback;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index e57db56..4c292e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -39,7 +39,6 @@
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.external.CustomTileStatePersister;
 import com.android.systemui.qs.external.TileLifecycleManager;
@@ -513,18 +512,6 @@
         return null;
     }
 
-    @Override
-    public QSTileView createTileView(Context themedContext, QSTile tile, boolean collapsedView) {
-        for (int i = 0; i < mQsFactories.size(); i++) {
-            QSTileView view = mQsFactories.get(i)
-                    .createTileView(themedContext, tile, collapsedView);
-            if (view != null) {
-                return view;
-            }
-        }
-        throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
-    }
-
     /**
      * Check if a particular {@link CustomTile} has been added for a user and has not been removed
      * since.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index d889979..1501379 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -27,8 +27,6 @@
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.plugins.qs.QSTile.State;
 
 /**
  * Version of QSPanel that only shows N Quick Tiles in the QS Header.
@@ -91,19 +89,6 @@
         return !mExpanded;
     }
 
-    @Override
-    protected void drawTile(QSPanelControllerBase.TileRecord r, State state) {
-        if (state instanceof SignalState) {
-            SignalState copy = new SignalState();
-            state.copyTo(copy);
-            // No activity shown in the quick panel.
-            copy.activityIn = false;
-            copy.activityOut = false;
-            state = copy;
-        }
-        super.drawTile(r, state);
-    }
-
     public void setMaxTiles(int maxTiles) {
         mMaxTiles = maxTiles;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 2d54313..585136a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -32,6 +32,7 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.ArrayList;
@@ -55,10 +56,10 @@
             @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
                     Provider<Boolean> usingCollapsedLandscapeMediaProvider,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-            DumpManager dumpManager
+            DumpManager dumpManager, SplitShadeStateController splitShadeStateController
     ) {
         super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
-                uiEventLogger, qsLogger, dumpManager);
+                uiEventLogger, qsLogger, dumpManager, splitShadeStateController);
         mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
deleted file mode 100644
index b609df5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.SlashImageView;
-
-/** View that represents a custom quick settings tile for displaying signal info (wifi/cell). **/
-public class SignalTileView extends QSIconViewImpl {
-    private static final long DEFAULT_DURATION = new ValueAnimator().getDuration();
-    private static final long SHORT_DURATION = DEFAULT_DURATION / 3;
-
-    protected FrameLayout mIconFrame;
-    protected ImageView mSignal;
-    private ImageView mOverlay;
-    private ImageView mIn;
-    private ImageView mOut;
-
-    private int mWideOverlayIconStartPadding;
-    private int mSignalIndicatorToIconFrameSpacing;
-
-    public SignalTileView(Context context) {
-        super(context);
-
-        mIn = addTrafficView(R.drawable.ic_qs_signal_in);
-        mOut = addTrafficView(R.drawable.ic_qs_signal_out);
-
-        setClipChildren(false);
-        setClipToPadding(false);
-
-        mWideOverlayIconStartPadding = context.getResources().getDimensionPixelSize(
-                R.dimen.wide_type_icon_start_padding_qs);
-        mSignalIndicatorToIconFrameSpacing = context.getResources().getDimensionPixelSize(
-                R.dimen.signal_indicator_to_icon_frame_spacing);
-    }
-
-    private ImageView addTrafficView(int icon) {
-        final ImageView traffic = new ImageView(mContext);
-        traffic.setImageResource(icon);
-        traffic.setAlpha(0f);
-        addView(traffic);
-        return traffic;
-    }
-
-    @Override
-    protected View createIcon() {
-        mIconFrame = new FrameLayout(mContext);
-        mSignal = createSlashImageView(mContext);
-        mIconFrame.addView(mSignal);
-        mOverlay = new ImageView(mContext);
-        mIconFrame.addView(mOverlay, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-        return mIconFrame;
-    }
-
-    protected SlashImageView createSlashImageView(Context context) {
-        return new SlashImageView(context);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        int hs = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.EXACTLY);
-        int ws = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.AT_MOST);
-        mIn.measure(ws, hs);
-        mOut.measure(ws, hs);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        layoutIndicator(mIn);
-        layoutIndicator(mOut);
-    }
-
-    @Override
-    protected int getIconMeasureMode() {
-        return MeasureSpec.AT_MOST;
-    }
-
-    private void layoutIndicator(View indicator) {
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        int left, right;
-        if (isRtl) {
-            right = getLeft() - mSignalIndicatorToIconFrameSpacing;
-            left = right - indicator.getMeasuredWidth();
-        } else {
-            left = getRight() + mSignalIndicatorToIconFrameSpacing;
-            right = left + indicator.getMeasuredWidth();
-        }
-        indicator.layout(
-                left,
-                mIconFrame.getBottom() - indicator.getMeasuredHeight(),
-                right,
-                mIconFrame.getBottom());
-    }
-
-    @Override
-    public void setIcon(State state, boolean allowAnimations) {
-        final SignalState s = (SignalState) state;
-        setIcon(mSignal, s, allowAnimations);
-
-        if (s.overlayIconId > 0) {
-            mOverlay.setVisibility(VISIBLE);
-            mOverlay.setImageResource(s.overlayIconId);
-        } else {
-            mOverlay.setVisibility(GONE);
-        }
-        if (s.overlayIconId > 0 && s.isOverlayIconWide) {
-            mSignal.setPaddingRelative(mWideOverlayIconStartPadding, 0, 0, 0);
-        } else {
-            mSignal.setPaddingRelative(0, 0, 0, 0);
-        }
-        final boolean shouldAnimate = allowAnimations && isShown();
-        // Do not show activity indicators
-//        setVisibility(mIn, shouldAnimate, s.activityIn);
-//        setVisibility(mOut, shouldAnimate, s.activityOut);
-    }
-
-    private void setVisibility(View view, boolean shown, boolean visible) {
-        final float newAlpha = shown && visible ? 1 : 0;
-        if (view.getAlpha() == newAlpha) return;
-        if (shown) {
-            view.animate()
-                .setDuration(visible ? SHORT_DURATION : DEFAULT_DURATION)
-                .alpha(newAlpha)
-                .start();
-        } else {
-            view.setAlpha(newAlpha);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
deleted file mode 100644
index 9011853..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.ColorInt;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.FloatProperty;
-
-public class SlashDrawable extends Drawable {
-
-    public static final float CORNER_RADIUS = 1f;
-
-    private final Path mPath = new Path();
-    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
-    // These values are derived in un-rotated (vertical) orientation
-    private static final float SLASH_WIDTH = 1.8384776f;
-    private static final float SLASH_HEIGHT = 28f;
-    private static final float CENTER_X = 10.65f;
-    private static final float CENTER_Y = 11.869239f;
-    private static final float SCALE = 24f;
-
-    // Bottom is derived during animation
-    private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE;
-    private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE;
-    private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE;
-    // Draw the slash washington-monument style; rotate to no-u-turn style
-    private static final float DEFAULT_ROTATION = -45f;
-
-    private Drawable mDrawable;
-    private final RectF mSlashRect = new RectF(0, 0, 0, 0);
-    private float mRotation;
-    private boolean mSlashed;
-    @Nullable
-    private Mode mTintMode;
-    @Nullable
-    private ColorStateList mTintList;
-    private boolean mAnimationEnabled = true;
-
-    public SlashDrawable(Drawable d) {
-        mDrawable = d;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mDrawable != null ? mDrawable.getIntrinsicHeight(): 0;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mDrawable != null ? mDrawable.getIntrinsicWidth(): 0;
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
-        mDrawable.setBounds(bounds);
-    }
-
-    public void setDrawable(Drawable d) {
-        mDrawable = d;
-        mDrawable.setCallback(getCallback());
-        mDrawable.setBounds(getBounds());
-        if (mTintMode != null) mDrawable.setTintMode(mTintMode);
-        if (mTintList != null) mDrawable.setTintList(mTintList);
-        invalidateSelf();
-    }
-
-    public void setRotation(float rotation) {
-        if (mRotation == rotation) return;
-        mRotation = rotation;
-        invalidateSelf();
-    }
-
-    public void setAnimationEnabled(boolean enabled) {
-        mAnimationEnabled = enabled;
-    }
-
-    // Animate this value on change
-    private float mCurrentSlashLength;
-    private final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") {
-        @Override
-        public void setValue(SlashDrawable object, float value) {
-            object.mCurrentSlashLength = value;
-        }
-
-        @Override
-        public Float get(SlashDrawable object) {
-            return object.mCurrentSlashLength;
-        }
-    };
-
-    public void setSlashed(boolean slashed) {
-        if (mSlashed == slashed) return;
-
-        mSlashed = slashed;
-
-        final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f;
-        final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE;
-
-        if (mAnimationEnabled) {
-            ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
-            anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf());
-            anim.setDuration(QS_ANIM_LENGTH);
-            anim.start();
-        } else {
-            mCurrentSlashLength = end;
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        canvas.save();
-        Matrix m = new Matrix();
-        final int width = getBounds().width();
-        final int height = getBounds().height();
-        final float radiusX = scale(CORNER_RADIUS, width);
-        final float radiusY = scale(CORNER_RADIUS, height);
-        updateRect(
-                scale(LEFT, width),
-                scale(TOP, height),
-                scale(RIGHT, width),
-                scale(TOP + mCurrentSlashLength, height)
-        );
-
-        mPath.reset();
-        // Draw the slash vertically
-        mPath.addRoundRect(mSlashRect, radiusX, radiusY, Direction.CW);
-        // Rotate -45 + desired rotation
-        m.setRotate(mRotation + DEFAULT_ROTATION, width / 2, height / 2);
-        mPath.transform(m);
-        canvas.drawPath(mPath, mPaint);
-
-        // Rotate back to vertical
-        m.setRotate(-mRotation - DEFAULT_ROTATION, width / 2, height / 2);
-        mPath.transform(m);
-
-        // Draw another rect right next to the first, for clipping
-        m.setTranslate(mSlashRect.width(), 0);
-        mPath.transform(m);
-        mPath.addRoundRect(mSlashRect, 1.0f * width, 1.0f * height, Direction.CW);
-        m.setRotate(mRotation + DEFAULT_ROTATION, width / 2, height / 2);
-        mPath.transform(m);
-        canvas.clipOutPath(mPath);
-
-        mDrawable.draw(canvas);
-        canvas.restore();
-    }
-
-    private float scale(float frac, int width) {
-        return frac * width;
-    }
-
-    private void updateRect(float left, float top, float right, float bottom) {
-        mSlashRect.left = left;
-        mSlashRect.top = top;
-        mSlashRect.right = right;
-        mSlashRect.bottom = bottom;
-    }
-
-    @Override
-    public void setTint(@ColorInt int tintColor) {
-        super.setTint(tintColor);
-        mDrawable.setTint(tintColor);
-        mPaint.setColor(tintColor);
-    }
-
-    @Override
-    public void setTintList(@Nullable ColorStateList tint) {
-        mTintList = tint;
-        super.setTintList(tint);
-        setDrawableTintList(tint);
-        mPaint.setColor(tint.getDefaultColor());
-        invalidateSelf();
-    }
-
-    protected void setDrawableTintList(@Nullable ColorStateList tint) {
-        mDrawable.setTintList(tint);
-    }
-
-    @Override
-    public void setTintMode(@NonNull Mode tintMode) {
-        mTintMode = tintMode;
-        super.setTintMode(tintMode);
-        mDrawable.setTintMode(tintMode);
-    }
-
-    @Override
-    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
-        mDrawable.setAlpha(alpha);
-        mPaint.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-        mDrawable.setColorFilter(colorFilter);
-        mPaint.setColorFilter(colorFilter);
-    }
-
-    @Override
-    public int getOpacity() {
-        return 255;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
index a316e6a..edc16be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
@@ -18,17 +18,11 @@
 
 import android.content.Context
 import android.text.TextUtils
-import com.android.systemui.plugins.qs.QSIconView
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.tileimpl.QSTileViewImpl
 
-/**
- * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
- */
-class CustomizeTileView(
-    context: Context,
-    icon: QSIconView
-) : QSTileViewImpl(context, icon, collapsed = false) {
+/** Class for displaying tiles in [QSCustomizer] with the new design (labels on the side). */
+class CustomizeTileView(context: Context) : QSTileViewImpl(context, collapsed = false) {
 
     var showAppLabel = false
         set(value) {
@@ -68,4 +62,4 @@
     fun changeState(state: QSTile.State) {
         handleStateChanged(state)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 596475e..e890170 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -54,7 +54,6 @@
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.dagger.QSThemedContext;
 import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
 import com.android.systemui.qs.tileimpl.QSTileViewImpl;
 
 import java.util.ArrayList;
@@ -297,7 +296,7 @@
         }
         FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
                 false);
-        View view = new CustomizeTileView(context, new QSIconViewImpl(context));
+        View view = new CustomizeTileView(context);
         frame.addView(view);
         return new Holder(frame);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 3432628..d2c51e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.R
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.qs.QSTileView
-import com.android.systemui.qs.tileimpl.QSIconViewImpl
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
 import com.android.systemui.qs.tileimpl.QSTileViewImpl
@@ -68,7 +67,7 @@
 
     private fun createTileView(tileData: TileData): QSTileView {
         val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
-        val tile = QSTileViewImpl(themedContext, QSIconViewImpl(themedContext), true)
+        val tile = QSTileViewImpl(themedContext, true)
         val state = QSTile.BooleanState().apply {
             label = tileData.label
             handlesLongClick = false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 39745c8..38e7972 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -222,16 +222,8 @@
                 str2 = state.label?.toString()
                 str3 = state.icon?.toString()
                 int1 = state.state
-                if (state is QSTile.SignalState) {
-                    bool1 = true
-                    bool2 = state.activityIn
-                    bool3 = state.activityOut
-                }
             },
-            {
-                "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." +
-                    if (bool1) " Activity in/out=$bool2/$bool3" else ""
-            }
+            { "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." }
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 6b23f5d..9c7a734 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -14,7 +14,6 @@
 
 package com.android.systemui.qs.tileimpl;
 
-import android.content.Context;
 import android.os.Build;
 import android.util.Log;
 
@@ -22,20 +21,18 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.util.leak.GarbageMonitor;
 
+import dagger.Lazy;
+
 import java.util.Map;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import dagger.Lazy;
-
 /**
  * A factory that creates Quick Settings tiles based on a tileSpec
  *
@@ -96,10 +93,4 @@
         Log.w(TAG, "No stock tile spec: " + tileSpec);
         return null;
     }
-
-    @Override
-    public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
-        QSIconView icon = tile.createTileView(context);
-        return new QSTileViewImpl(context, icon, collapsedView);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 7e45491..88c8e81 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -38,7 +38,6 @@
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
 
 import java.util.Objects;
 
@@ -114,8 +113,7 @@
 
     protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
         final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
-        if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
-                || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
+        if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))) {
             boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
             mLastIcon = icon;
             Drawable d = icon != null
@@ -135,15 +133,9 @@
                 ((Animatable2) lastDrawable).clearAnimationCallbacks();
             }
 
-            if (iv instanceof SlashImageView) {
-                ((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
-                ((SlashImageView) iv).setState(null, d);
-            } else {
-                iv.setImageDrawable(d);
-            }
+            iv.setImageDrawable(d);
 
             iv.setTag(R.id.qs_icon_tag, icon);
-            iv.setTag(R.id.qs_slash_tag, state.slash);
             iv.setPadding(0, padding, 0, padding);
             if (d instanceof Animatable2) {
                 Animatable2 a = (Animatable2) d;
@@ -177,12 +169,7 @@
             if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
                 animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
             } else {
-                if (iv instanceof AlphaControlledSlashImageView) {
-                    ((AlphaControlledSlashImageView)iv)
-                            .setFinalImageTintList(ColorStateList.valueOf(color));
-                } else {
-                    setTint(iv, color);
-                }
+                setTint(iv, color);
                 updateIcon(iv, state, allowAnimations);
             }
         } else {
@@ -195,11 +182,7 @@
     }
 
     private void animateGrayScale(int fromColor, int toColor, ImageView iv,
-        final Runnable endRunnable) {
-        if (iv instanceof AlphaControlledSlashImageView) {
-            ((AlphaControlledSlashImageView)iv)
-                    .setFinalImageTintList(ColorStateList.valueOf(toColor));
-        }
+            final Runnable endRunnable) {
         mColorAnimator.cancel();
         if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
             PropertyValuesHolder values = PropertyValuesHolder.ofInt("color", fromColor, toColor);
@@ -229,7 +212,7 @@
     }
 
     protected View createIcon() {
-        final ImageView icon = new SlashImageView(mContext);
+        final ImageView icon = new ImageView(mContext);
         icon.setId(android.R.id.icon);
         icon.setScaleType(ScaleType.FIT_CENTER);
         return icon;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1ca2a96..70df09d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -60,7 +60,6 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -262,16 +261,6 @@
     }
 
     /**
-     * Return the {@link QSIconView} to be used by this tile's view.
-     *
-     * @param context view context for the view
-     * @return icon view for this tile
-     */
-    public QSIconView createTileView(Context context) {
-        return new QSIconViewImpl(context);
-    }
-
-    /**
      * Is a startup check whether this device currently supports this tile.
      * Should not be used to conditionally hide tiles.  Only checked on tile
      * creation or whether should be shown in edit screen.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 764ef68..966d941 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -58,7 +58,6 @@
 private const val TAG = "QSTileViewImpl"
 open class QSTileViewImpl @JvmOverloads constructor(
     context: Context,
-    private val _icon: QSIconView,
     private val collapsed: Boolean = false
 ) : QSTileView(context), HeightOverrideable, LaunchableView {
 
@@ -73,10 +72,11 @@
         internal const val TILE_STATE_RES_PREFIX = "tile_states_"
     }
 
-    private var _position: Int = INVALID
+    private val icon: QSIconViewImpl = QSIconViewImpl(context)
+    private var position: Int = INVALID
 
     override fun setPosition(position: Int) {
-        _position = position
+        this.position = position
     }
 
     override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
@@ -173,7 +173,7 @@
         setPaddingRelative(startPadding, padding, padding, padding)
 
         val iconSize = resources.getDimensionPixelSize(R.dimen.qs_icon_size)
-        addView(_icon, LayoutParams(iconSize, iconSize))
+        addView(icon, LayoutParams(iconSize, iconSize))
 
         createAndAddLabels()
         createAndAddSideView()
@@ -204,7 +204,7 @@
         FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
 
         val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
-        _icon.layoutParams.apply {
+        icon.layoutParams.apply {
             height = iconSize
             width = iconSize
         }
@@ -291,7 +291,7 @@
     }
 
     override fun getIcon(): QSIconView {
-        return _icon
+        return icon
     }
 
     override fun getIconWithBackground(): View {
@@ -425,16 +425,16 @@
                 }
             }
         }
-        if (_position != INVALID) {
+        if (position != INVALID) {
             info.collectionItemInfo =
-                AccessibilityNodeInfo.CollectionItemInfo(_position, 1, 0, 1, false)
+                AccessibilityNodeInfo.CollectionItemInfo(position, 1, 0, 1, false)
         }
     }
 
     override fun toString(): String {
         val sb = StringBuilder(javaClass.simpleName).append('[')
         sb.append("locInScreen=(${locInScreen[0]}, ${locInScreen[1]})")
-        sb.append(", iconView=$_icon")
+        sb.append(", iconView=$icon")
         sb.append(", tileState=$tileState")
         sb.append("]")
         return sb.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
deleted file mode 100644
index f1e82b6..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs.tileimpl;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.widget.ImageView;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.plugins.qs.QSTile.SlashState;
-import com.android.systemui.qs.SlashDrawable;
-
-public class SlashImageView extends ImageView {
-
-    @Nullable
-    @VisibleForTesting
-    protected SlashDrawable mSlash;
-    private boolean mAnimationEnabled = true;
-
-    public SlashImageView(Context context) {
-        super(context);
-    }
-
-    @Nullable
-    protected SlashDrawable getSlash() {
-        return mSlash;
-    }
-
-    protected void setSlash(SlashDrawable slash) {
-        mSlash = slash;
-    }
-
-    protected void ensureSlashDrawable() {
-        if (mSlash == null) {
-            mSlash = new SlashDrawable(getDrawable());
-            mSlash.setAnimationEnabled(mAnimationEnabled);
-            super.setImageDrawable(mSlash);
-        }
-    }
-
-    @Override
-    public void setImageDrawable(@Nullable Drawable drawable) {
-        if (drawable == null) {
-            mSlash = null;
-            super.setImageDrawable(null);
-        } else if (mSlash == null) {
-            setImageLevel(drawable.getLevel());
-            super.setImageDrawable(drawable);
-        } else {
-            mSlash.setAnimationEnabled(mAnimationEnabled);
-            mSlash.setDrawable(drawable);
-        }
-    }
-
-    protected void setImageViewDrawable(SlashDrawable slash) {
-        super.setImageDrawable(slash);
-    }
-
-    public void setAnimationEnabled(boolean enabled) {
-        mAnimationEnabled = enabled;
-    }
-
-    public boolean getAnimationEnabled() {
-        return mAnimationEnabled;
-    }
-
-    private void setSlashState(@NonNull SlashState slashState) {
-        ensureSlashDrawable();
-        mSlash.setRotation(slashState.rotation);
-        mSlash.setSlashed(slashState.isSlashed);
-    }
-
-    public void setState(@Nullable SlashState state, @Nullable Drawable drawable) {
-        if (state != null) {
-            setImageDrawable(drawable);
-            setSlashState(state);
-        } else {
-            mSlash = null;
-            setImageDrawable(drawable);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 26912f8..83b09be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -151,10 +151,6 @@
         }
         state.dualTarget = true;
         state.value = enabled;
-        if (state.slash == null) {
-            state.slash = new SlashState();
-        }
-        state.slash.isSlashed = !enabled;
         state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
         state.secondaryLabel = TextUtils.emptyIfNull(
                 getSecondaryLabel(enabled, connecting, connected, state.isTransient));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index ffe5489..fb2f2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -46,10 +46,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.AlphaControlledSignalTileView;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.logging.QSLogger;
@@ -68,7 +66,7 @@
 import javax.inject.Inject;
 
 /** Quick settings tile: Internet **/
-public class InternetTile extends QSTileImpl<SignalState> {
+public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
 
     public static final String TILE_SPEC = "internet";
 
@@ -114,18 +112,13 @@
     }
 
     @Override
-    public SignalState newTileState() {
-        SignalState s = new SignalState();
+    public BooleanState newTileState() {
+        BooleanState s = new BooleanState();
         s.forceExpandIcon = true;
         return s;
     }
 
     @Override
-    public QSIconView createTileView(Context context) {
-        return new AlphaControlledSignalTileView(context);
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return WIFI_SETTINGS;
     }
@@ -453,7 +446,7 @@
     }
 
     @Override
-    protected void handleUpdateState(SignalState state, Object arg) {
+    protected void handleUpdateState(BooleanState state, Object arg) {
         mQSLogger.logInternetTileUpdate(
                 getTileSpec(), mLastTileState, arg == null ? "null" : arg.toString());
         if (arg instanceof CellularCallbackInfo) {
@@ -506,18 +499,13 @@
         }
     }
 
-    private void handleUpdateWifiState(SignalState state, Object arg) {
+    private void handleUpdateWifiState(BooleanState state, Object arg) {
         WifiCallbackInfo cb = (WifiCallbackInfo) arg;
         if (DEBUG) {
             Log.d(TAG, "handleUpdateWifiState: " + "WifiCallbackInfo = " + cb.toString());
         }
         boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
         boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
-        if (state.slash == null) {
-            state.slash = new SlashState();
-            state.slash.rotation = 6;
-        }
-        state.slash.isSlashed = false;
         state.secondaryLabel = getSecondaryLabel(cb.mIsTransient, removeDoubleQuotes(cb.mSsid));
         state.state = Tile.STATE_ACTIVE;
         state.dualTarget = true;
@@ -555,7 +543,6 @@
             state.icon = ResourceIcon.get(
                 com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
         } else if (!state.value) {
-            state.slash.isSlashed = true;
             state.state = Tile.STATE_INACTIVE;
             state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
         } else if (wifiConnected) {
@@ -580,11 +567,11 @@
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
         state.expandedAccessibilityClassName = Switch.class.getName();
         if (DEBUG) {
-            Log.d(TAG, "handleUpdateWifiState: " + "SignalState = " + state.toString());
+            Log.d(TAG, "handleUpdateWifiState: " + "BooleanState = " + state.toString());
         }
     }
 
-    private void handleUpdateCellularState(SignalState state, Object arg) {
+    private void handleUpdateCellularState(BooleanState state, Object arg) {
         CellularCallbackInfo cb = (CellularCallbackInfo) arg;
         if (DEBUG) {
             Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
@@ -623,11 +610,11 @@
             state.stateDescription = state.secondaryLabel;
         }
         if (DEBUG) {
-            Log.d(TAG, "handleUpdateCellularState: " + "SignalState = " + state.toString());
+            Log.d(TAG, "handleUpdateCellularState: " + "BooleanState = " + state.toString());
         }
     }
 
-    private void handleUpdateEthernetState(SignalState state, Object arg) {
+    private void handleUpdateEthernetState(BooleanState state, Object arg) {
         EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
         if (DEBUG) {
             Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
@@ -641,7 +628,7 @@
         state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
         state.secondaryLabel = cb.mEthernetContentDescription;
         if (DEBUG) {
-            Log.d(TAG, "handleUpdateEthernetState: " + "SignalState = " + state.toString());
+            Log.d(TAG, "handleUpdateEthernetState: " + "BooleanState = " + state.toString());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 3b2f8b7..956e7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.tiles
 
-import android.content.Context
 import android.content.Intent
 import android.os.Handler
 import android.os.Looper
@@ -28,10 +27,8 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.qs.QSIconView
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.AlphaControlledSignalTileView
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
@@ -59,7 +56,7 @@
     private val internetDialogFactory: InternetDialogFactory,
     private val accessPointController: AccessPointController,
 ) :
-    QSTileImpl<QSTile.SignalState>(
+    QSTileImpl<QSTile.BooleanState>(
         host,
         uiEventLogger,
         backgroundLooper,
@@ -79,14 +76,11 @@
         }
     }
 
-    override fun createTileView(context: Context): QSIconView =
-        AlphaControlledSignalTileView(context)
-
     override fun getTileLabel(): CharSequence =
         mContext.getString(R.string.quick_settings_internet_label)
 
-    override fun newTileState(): QSTile.SignalState {
-        return QSTile.SignalState().also { it.forceExpandIcon = true }
+    override fun newTileState(): QSTile.BooleanState {
+        return QSTile.BooleanState().also { it.forceExpandIcon = true }
     }
 
     override fun handleClick(view: View?) {
@@ -100,7 +94,7 @@
         }
     }
 
-    override fun handleUpdateState(state: QSTile.SignalState, arg: Any?) {
+    override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
         state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
 
         model.applyTo(state, mContext)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 83c5688..8b69f61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -112,9 +112,6 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        if (state.slash == null) {
-            state.slash = new SlashState();
-        }
         final boolean locationEnabled =  mController.isLocationEnabled();
 
         // Work around for bug 15916487: don't show location tile on top of lock screen. After the
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index 1eb317a..5b0237f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -130,10 +130,6 @@
         state.value = enabled;
         state.label = mContext.getString(R.string.quick_settings_onehanded_label);
         state.icon = mIcon;
-        if (state.slash == null) {
-            state.slash = new SlashState();
-        }
-        state.slash.isSlashed = !state.value;
         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.contentDescription = state.label;
         state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 544e6ad..e382eca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
 
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
 
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -210,7 +211,7 @@
         public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
             Log.i(TAG, "Successfully retrieved wallet cards.");
             mIsWalletUpdating = false;
-            List<WalletCard> cards = response.getWalletCards();
+            List<WalletCard> cards = getPaymentCards(response.getWalletCards());
             if (cards.isEmpty()) {
                 Log.d(TAG, "No wallet cards exist.");
                 mCardViewDrawable = null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 21da596..c8c3c30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -122,10 +122,6 @@
             onManagedProfileRemoved();
         }
 
-        if (state.slash == null) {
-            state.slash = new SlashState();
-        }
-
         if (arg instanceof Boolean) {
             state.value = (Boolean) arg;
         } else {
@@ -133,11 +129,6 @@
         }
 
         state.icon = mIcon;
-        if (state.value) {
-            state.slash.isSlashed = false;
-        } else {
-            state.slash.isSlashed = true;
-        }
         state.label = getTileLabel();
         state.contentDescription = state.label;
         state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
new file mode 100644
index 0000000..e9f907c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
@@ -0,0 +1,30 @@
+package com.android.systemui.qs.tiles.base.actions
+
+import android.content.Intent
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/**
+ * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard
+ * dismissing and tile from-view animations.
+ */
+@SysUISingleton
+class QSTileIntentUserActionHandler
+@Inject
+constructor(private val activityStarter: ActivityStarter) {
+
+    fun handle(userAction: QSTileUserAction, intent: Intent) {
+        val animationController: ActivityLaunchAnimator.Controller? =
+            userAction.view?.let {
+                ActivityLaunchAnimator.Controller.fromView(
+                    it,
+                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+                )
+            }
+        activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
new file mode 100644
index 0000000..d6c9705
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
@@ -0,0 +1,14 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import androidx.annotation.WorkerThread
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+
+interface QSTileDataToStateMapper<DATA_TYPE> {
+
+    /**
+     * Maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer. It's called
+     * on a background thread, so it's safe to perform long running operations there.
+     */
+    @WorkerThread fun map(config: QSTileConfig, data: DATA_TYPE): QSTileState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
new file mode 100644
index 0000000..c2a75fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
@@ -0,0 +1,185 @@
+package com.android.systemui.qs.tiles.base.viewmodel
+
+import androidx.annotation.CallSuper
+import androidx.annotation.VisibleForTesting
+import com.android.internal.util.Preconditions
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.util.kotlin.sample
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides a hassle-free way to implement new tiles according to current System UI architecture
+ * standards. THis ViewModel is cheap to instantiate and does nothing until it's moved to
+ * [QSTileLifecycle.ALIVE] state.
+ */
+abstract class BaseQSTileViewModel<DATA_TYPE>
+@VisibleForTesting
+constructor(
+    final override val config: QSTileConfig,
+    private val userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+    private val tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+    private val mapper: QSTileDataToStateMapper<DATA_TYPE>,
+    private val backgroundDispatcher: CoroutineDispatcher,
+    private val tileScope: CoroutineScope,
+) : QSTileViewModel {
+
+    /**
+     * @param config contains all the static information (like TileSpec) about the tile.
+     * @param userActionInteractor encapsulates user input processing logic. Use it to start
+     *   activities, show dialogs or otherwise update the tile state.
+     * @param tileDataInteractor provides [DATA_TYPE] and its availability.
+     * @param backgroundDispatcher is used to run the internal [DATA_TYPE] processing and call
+     *   interactors methods. This should likely to be @Background CoroutineDispatcher.
+     * @param mapper maps [DATA_TYPE] to the [QSTileState] that is then displayed by the View layer.
+     *   It's called in [backgroundDispatcher], so it's safe to perform long running operations
+     *   there.
+     */
+    constructor(
+        config: QSTileConfig,
+        userActionInteractor: QSTileUserActionInteractor<DATA_TYPE>,
+        tileDataInteractor: QSTileDataInteractor<DATA_TYPE>,
+        mapper: QSTileDataToStateMapper<DATA_TYPE>,
+        backgroundDispatcher: CoroutineDispatcher,
+    ) : this(
+        config,
+        userActionInteractor,
+        tileDataInteractor,
+        mapper,
+        backgroundDispatcher,
+        CoroutineScope(SupervisorJob())
+    )
+
+    private val userInputs: MutableSharedFlow<QSTileUserAction> =
+        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+    private val userIds: MutableSharedFlow<Int> =
+        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+    private val forceUpdates: MutableSharedFlow<Unit> =
+        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+    private lateinit var tileData: SharedFlow<DATA_TYPE>
+
+    override lateinit var state: SharedFlow<QSTileState>
+    override val isAvailable: StateFlow<Boolean> =
+        tileDataInteractor
+            .availability()
+            .flowOn(backgroundDispatcher)
+            .stateIn(
+                tileScope,
+                SharingStarted.WhileSubscribed(),
+                false,
+            )
+
+    private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
+
+    @CallSuper
+    override fun forceUpdate() {
+        Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+        forceUpdates.tryEmit(Unit)
+    }
+
+    @CallSuper
+    override fun onUserIdChanged(userId: Int) {
+        Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+        userIds.tryEmit(userId)
+    }
+
+    @CallSuper
+    override fun onActionPerformed(userAction: QSTileUserAction) {
+        Preconditions.checkState(tileData.replayCache.isNotEmpty())
+        Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+        userInputs.tryEmit(userAction)
+    }
+
+    @CallSuper
+    override fun onLifecycle(lifecycle: QSTileLifecycle) {
+        when (lifecycle) {
+            QSTileLifecycle.ALIVE -> {
+                Preconditions.checkState(currentLifeState == QSTileLifecycle.DEAD)
+                tileData = createTileDataFlow()
+                state =
+                    tileData
+                        // TODO(b/299908705): log data and corresponding tile state
+                        .map { mapper.map(config, it) }
+                        .flowOn(backgroundDispatcher)
+                        .shareIn(
+                            tileScope,
+                            SharingStarted.WhileSubscribed(),
+                            replay = 1,
+                        )
+            }
+            QSTileLifecycle.DEAD -> {
+                Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE)
+                tileScope.coroutineContext.cancelChildren()
+            }
+        }
+        currentLifeState = lifecycle
+    }
+
+    private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
+        userIds
+            .flatMapLatest { userId ->
+                merge(
+                        userInputFlow(),
+                        forceUpdates.map { StateUpdateTrigger.ForceUpdate },
+                    )
+                    .onStart { emit(StateUpdateTrigger.InitialRequest) }
+                    .map { trigger -> QSTileDataRequest(userId, trigger) }
+            }
+            .flatMapLatest { request ->
+                // 1) get an updated data source
+                // 2) process user input, possibly triggering new data to be emitted
+                // This handles the case when the data isn't buffered in the interactor
+                // TODO(b/299908705): Log events that trigger data flow to update
+                val dataFlow = tileDataInteractor.tileData(request)
+                if (request.trigger is StateUpdateTrigger.UserAction<*>) {
+                    userActionInteractor.handleInput(
+                        request.trigger.action,
+                        request.trigger.tileData as DATA_TYPE,
+                    )
+                }
+                dataFlow
+            }
+            .flowOn(backgroundDispatcher)
+            .shareIn(
+                tileScope,
+                SharingStarted.WhileSubscribed(),
+                replay = 1, // we only care about the most recent value
+            )
+
+    private fun userInputFlow(): Flow<StateUpdateTrigger> {
+        data class StateWithData<T>(val state: QSTileState, val data: T)
+
+        // Skip the input until there is some data
+        return userInputs.sample(
+            state.combine(tileData) { state, data -> StateWithData(state, data) }
+        ) { input, stateWithData ->
+            StateUpdateTrigger.UserAction(input, stateWithData.state, stateWithData.data)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
index 39db703..1d5c1bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileLifecycle.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.qs.tiles.viewmodel
 
 enum class QSTileLifecycle {
-    ON_CREATE,
-    ON_DESTROY,
+    ALIVE,
+    DEAD,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index 49077f3..d66d0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -1,28 +1,35 @@
 package com.android.systemui.qs.tiles.viewmodel
 
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
 
 /**
  * Represents tiles behaviour logic. This ViewModel is a connection between tile view and data
- * layers.
+ * layers. All direct inheritors must be added to the [QSTileViewModelInterfaceComplianceTest] class
+ * to pass compliance tests.
+ *
+ * All methods of this view model should be considered running on the main thread. This means no
+ * synchronous long running operations are permitted in any method.
  */
 interface QSTileViewModel {
 
     /**
-     * State of the tile to be shown by the view. Favor reactive consumption over the
-     * [StateFlow.value], because there is no guarantee that current value would be available at any
-     * time.
+     * State of the tile to be shown by the view. It's guaranteed that it's only accessed between
+     * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD].
      */
-    val state: StateFlow<QSTileState>
+    val state: SharedFlow<QSTileState>
 
     val config: QSTileConfig
 
-    val isAvailable: Flow<Boolean>
+    /**
+     * Specifies whether this device currently supports this tile. This might be called outside of
+     * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds (for example in Edit Mode).
+     */
+    val isAvailable: StateFlow<Boolean>
 
     /**
      * Handles ViewModel lifecycle. Implementations should be inactive outside of
-     * [QSTileLifecycle.ON_CREATE] and [QSTileLifecycle.ON_DESTROY] bounds.
+     * [QSTileLifecycle.ALIVE] and [QSTileLifecycle.DEAD] bounds.
      */
     fun onLifecycle(lifecycle: QSTileLifecycle)
 
@@ -33,9 +40,17 @@
      */
     fun onUserIdChanged(userId: Int)
 
-    /** Triggers emit of the new [QSTileState] in [state]. */
+    /** Triggers the emission of the new [QSTileState] in a [state]. */
     fun forceUpdate()
 
     /** Notifies underlying logic about user input. */
     fun onActionPerformed(userAction: QSTileUserAction)
 }
+
+/**
+ * Returns the immediate state of the tile or null if the state haven't been collected yet. Favor
+ * reactive consumption over the [currentState], because there is no guarantee that current value
+ * would be available at any time.
+ */
+val QSTileViewModel.currentState: QSTileState?
+    get() = state.replayCache.lastOrNull()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index d23beda..4465504 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -104,7 +104,6 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
@@ -117,7 +116,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
@@ -146,11 +144,11 @@
     private final SceneContainerFlags mSceneContainerFlags;
     private final Executor mMainExecutor;
     private final ShellInterface mShellInterface;
-    private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
     private final Lazy<ShadeViewController> mShadeViewControllerLazy;
     private SysUiState mSysUiState;
     private final Handler mHandler;
     private final Lazy<NavigationBarController> mNavBarControllerLazy;
+    private final ScreenPinningRequest mScreenPinningRequest;
     private final NotificationShadeWindowController mStatusBarWinController;
     private final Provider<SceneInteractor> mSceneInteractor;
 
@@ -185,9 +183,7 @@
         @Override
         public void startScreenPinning(int taskId) {
             verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
-                    mCentralSurfacesOptionalLazy.get().ifPresent(
-                            statusBar -> statusBar.showScreenPinningRequest(taskId,
-                                    false /* allowCancel */)));
+                    mScreenPinningRequest.showPrompt(taskId, false /* allowCancel */));
         }
 
         @Override
@@ -206,40 +202,38 @@
         public void onStatusBarTouchEvent(MotionEvent event) {
             verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> {
                 // TODO move this logic to message queue
-                mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
-                    if (event.getActionMasked() == ACTION_DOWN) {
-                        mShadeViewControllerLazy.get().startExpandLatencyTracking();
+                if (event.getActionMasked() == ACTION_DOWN) {
+                    mShadeViewControllerLazy.get().startExpandLatencyTracking();
+                }
+                mHandler.post(() -> {
+                    int action = event.getActionMasked();
+                    if (action == ACTION_DOWN) {
+                        mInputFocusTransferStarted = true;
+                        mInputFocusTransferStartY = event.getY();
+                        mInputFocusTransferStartMillis = event.getEventTime();
+
+                        // If scene framework is enabled, set the scene container window to
+                        // visible and let the touch "slip" into that window.
+                        if (mSceneContainerFlags.isEnabled()) {
+                            mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+                        } else {
+                            mShadeViewControllerLazy.get().startInputFocusTransfer();
+                        }
                     }
-                    mHandler.post(() -> {
-                        int action = event.getActionMasked();
-                        if (action == ACTION_DOWN) {
-                            mInputFocusTransferStarted = true;
-                            mInputFocusTransferStartY = event.getY();
-                            mInputFocusTransferStartMillis = event.getEventTime();
+                    if (action == ACTION_UP || action == ACTION_CANCEL) {
+                        mInputFocusTransferStarted = false;
 
-                            // If scene framework is enabled, set the scene container window to
-                            // visible and let the touch "slip" into that window.
-                            if (mSceneContainerFlags.isEnabled()) {
-                                mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+                        if (!mSceneContainerFlags.isEnabled()) {
+                            float velocity = (event.getY() - mInputFocusTransferStartY)
+                                    / (event.getEventTime() - mInputFocusTransferStartMillis);
+                            if (action == ACTION_CANCEL) {
+                                mShadeViewControllerLazy.get().cancelInputFocusTransfer();
                             } else {
-                                centralSurfaces.onInputFocusTransfer(
-                                        mInputFocusTransferStarted, false /* cancel */,
-                                        0 /* velocity */);
+                                mShadeViewControllerLazy.get().finishInputFocusTransfer(velocity);
                             }
                         }
-                        if (action == ACTION_UP || action == ACTION_CANCEL) {
-                            mInputFocusTransferStarted = false;
-
-                            if (!mSceneContainerFlags.isEnabled()) {
-                                float velocity = (event.getY() - mInputFocusTransferStartY)
-                                        / (event.getEventTime() - mInputFocusTransferStartMillis);
-                                centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted,
-                                        action == ACTION_CANCEL,
-                                        velocity);
-                            }
-                        }
-                        event.recycle();
-                    });
+                    }
+                    event.recycle();
                 });
             });
         }
@@ -247,8 +241,7 @@
         @Override
         public void onStatusBarTrackpadEvent(MotionEvent event) {
             verifyCallerAndClearCallingIdentityPostMain("onStatusBarTrackpadEvent", () ->
-                    mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces ->
-                            centralSurfaces.onStatusBarTrackpadEvent(event)));
+                    mShadeViewControllerLazy.get().handleExternalTouch(event));
         }
 
         @Override
@@ -493,9 +486,9 @@
             notifySystemUiStateFlags(mSysUiState.getFlags());
 
             notifyConnectionChanged();
-            if (mLatchForOnUserChanging != null) {
-                mLatchForOnUserChanging.countDown();
-                mLatchForOnUserChanging = null;
+            if (mDoneUserChanging != null) {
+                mDoneUserChanging.run();
+                mDoneUserChanging = null;
             }
         }
 
@@ -551,14 +544,14 @@
         }
     };
 
-    private CountDownLatch mLatchForOnUserChanging;
+    private Runnable mDoneUserChanging;
     private final UserTracker.Callback mUserChangedCallback =
             new UserTracker.Callback() {
                 @Override
                 public void onUserChanging(int newUser, @NonNull Context userContext,
-                        CountDownLatch latch) {
+                        @NonNull Runnable resultCallback) {
                     mConnectionBackoffAttempts = 0;
-                    mLatchForOnUserChanging = latch;
+                    mDoneUserChanging = resultCallback;
                     internalConnectToCurrentUser("User changed");
                 }
             };
@@ -570,8 +563,8 @@
             CommandQueue commandQueue,
             ShellInterface shellInterface,
             Lazy<NavigationBarController> navBarControllerLazy,
-            Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             Lazy<ShadeViewController> shadeViewControllerLazy,
+            ScreenPinningRequest screenPinningRequest,
             NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController,
             SysUiState sysUiState,
@@ -597,10 +590,10 @@
         mSceneContainerFlags = sceneContainerFlags;
         mMainExecutor = mainExecutor;
         mShellInterface = shellInterface;
-        mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
         mShadeViewControllerLazy = shadeViewControllerLazy;
         mHandler = new Handler();
         mNavBarControllerLazy = navBarControllerLazy;
+        mScreenPinningRequest = screenPinningRequest;
         mStatusBarWinController = statusBarWinController;
         mSceneInteractor = sceneInteractor;
         mUserTracker = userTracker;
@@ -771,10 +764,8 @@
     public void cleanupAfterDeath() {
         if (mInputFocusTransferStarted) {
             mHandler.post(() -> {
-                mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
-                    mInputFocusTransferStarted = false;
-                    centralSurfaces.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
-                });
+                mInputFocusTransferStarted = false;
+                mShadeViewControllerLazy.get().cancelInputFocusTransfer();
             });
         }
         startConnectionToCurrentUser();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 346acf9..3577c00 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -53,29 +53,30 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.ArrayList;
-import java.util.Optional;
 
 import javax.inject.Inject;
 
 import dagger.Lazy;
 
+@SysUISingleton
 public class ScreenPinningRequest implements View.OnClickListener,
-        NavigationModeController.ModeChangedListener {
+        NavigationModeController.ModeChangedListener, CoreStartable {
     private static final String TAG = "ScreenPinningRequest";
 
     private final Context mContext;
-    private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
-
+    private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
     private final AccessibilityManager mAccessibilityService;
     private final WindowManager mWindowManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
@@ -98,12 +99,12 @@
     @Inject
     public ScreenPinningRequest(
             Context context,
-            Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             NavigationModeController navigationModeController,
+            Lazy<NavigationBarController> navigationBarControllerLazy,
             BroadcastDispatcher broadcastDispatcher,
             UserTracker userTracker) {
         mContext = context;
-        mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+        mNavigationBarControllerLazy = navigationBarControllerLazy;
         mAccessibilityService = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
         mWindowManager = (WindowManager)
@@ -113,6 +114,9 @@
         mUserTracker = userTracker;
     }
 
+    @Override
+    public void start() {}
+
     public void clearPrompt() {
         if (mRequestWindow != null) {
             mWindowManager.removeView(mRequestWindow);
@@ -144,7 +148,8 @@
         mNavBarMode = mode;
     }
 
-    public void onConfigurationChanged() {
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
         if (mRequestWindow != null) {
             mRequestWindow.onConfigurationChanged();
         }
@@ -282,15 +287,14 @@
                         .setVisibility(View.INVISIBLE);
             }
 
-            final Optional<CentralSurfaces> centralSurfacesOptional =
-                    mCentralSurfacesOptionalLazy.get();
-            boolean recentsVisible =
-                    centralSurfacesOptional.map(CentralSurfaces::isOverviewEnabled).orElse(false);
+            int displayId = mContext.getDisplayId();
+            boolean overviewEnabled =
+                    mNavigationBarControllerLazy.get().isOverviewEnabled(displayId);
             boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
             int descriptionStringResId;
             if (QuickStepContract.isGesturalMode(mNavBarMode)) {
                 descriptionStringResId = R.string.screen_pinning_description_gestural;
-            } else if (recentsVisible) {
+            } else if (overviewEnabled) {
                 mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
                 mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
                 mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
@@ -307,7 +311,7 @@
             }
 
             NavigationBarView navigationBarView =
-                    centralSurfacesOptional.map(CentralSurfaces::getNavigationBarView).orElse(null);
+                    mNavigationBarControllerLazy.get().getNavigationBarView(displayId);
             if (navigationBarView != null) {
                 ((ImageView) mLayout.findViewById(R.id.screen_pinning_back_icon))
                         .setImageDrawable(navigationBarView.getBackDrawable());
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index b36ec32..0f3acaf 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -33,6 +33,7 @@
     includes =
         [
             BouncerSceneModule::class,
+            CommunalSceneModule::class,
             EmptySceneModule::class,
             GoneSceneModule::class,
             LockscreenSceneModule::class,
@@ -65,6 +66,7 @@
                 sceneKeys =
                     listOf(
                         SceneKey.Gone,
+                        SceneKey.Communal,
                         SceneKey.Lockscreen,
                         SceneKey.Bouncer,
                         SceneKey.Shade,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index 9a30aa6..3927873 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -32,5 +32,16 @@
         val fromScene: SceneKey,
         val toScene: SceneKey,
         val progress: Flow<Float>,
+
+        /**
+         * Whether the transition was originally triggered by user input rather than being
+         * programmatic. If this value is initially true, it will remain true until the transition
+         * fully completes, even if the user input that triggered the transition has ended. Any
+         * sub-transitions launched by this one will inherit this value. For example, if the user
+         * drags a pointer but does not exceed the threshold required to transition to another
+         * scene, this value will remain true after the pointer is no longer touching the screen and
+         * will be true in any transition created to animate back to the original position.
+         */
+        val isUserInputDriven: Boolean,
     ) : ObservableTransitionState()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 31597c1..4bc93a8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -16,9 +16,7 @@
 
 package com.android.systemui.scene.shared.model
 
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 
 /**
  * Defines interface for classes that can describe a "scene".
@@ -34,31 +32,26 @@
     val key: SceneKey
 
     /**
-     * Returns a mapping between [UserAction] and flows that emit a [SceneModel].
+     * The mapping between [UserAction] and destination [SceneModel]s.
      *
-     * When the scene framework detects the user action, it starts a transition to the scene
-     * described by the latest value in the flow that's mapped from that user action.
+     * When the scene framework detects a user action, if the current scene has a map entry for that
+     * user action, the framework starts a transition to the scene in the map.
      *
-     * Once the [Scene] becomes the current one, the scene framework will invoke this method and set
-     * up collectors to watch for new values emitted to each of the flows. If a value is added to
-     * the map at a given [UserAction], the framework will set up user input handling for that
-     * [UserAction] and, if such a user action is detected, the framework will initiate a transition
-     * to that [SceneModel].
+     * Once the [Scene] becomes the current one, the scene framework will read this property and set
+     * up a collector to watch for new mapping values. If every map entry provided by the scene, the
+     * framework will set up user input handling for its [UserAction] and, if such a user action is
+     * detected, initiate a transition to the specified [SceneModel].
      *
-     * Note that calling this method does _not_ mean that the given user action has occurred.
-     * Instead, the method is called before any user action/gesture is detected so that the
-     * framework can decide whether to set up gesture/input detectors/listeners for that type of
-     * user action.
+     * Note that reading from this method does _not_ mean that any user action has occurred.
+     * Instead, the property is read before any user action/gesture is detected so that the
+     * framework can decide whether to set up gesture/input detectors/listeners in case user actions
+     * of the given types ever occur.
      *
      * Note that a missing value for a specific [UserAction] means that the user action of the given
      * type is not currently active in the scene and should be ignored by the framework, while the
      * current scene is this one.
-     *
-     * The API is designed such that it's possible to emit ever-changing values for each
-     * [UserAction] to enable, disable, or change the destination scene of a given user action.
      */
-    fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
-        MutableStateFlow(emptyMap<UserAction, SceneModel>()).asStateFlow()
+    val destinationScenes: StateFlow<Map<UserAction, SceneModel>>
 }
 
 /** Enumerates all scene framework supported user actions. */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
index e7811e3..609d2b9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
@@ -16,7 +16,11 @@
 
 package com.android.systemui.scene.shared.model
 
-/** Keys of all known scenes. */
+/**
+ * Keys of all known scenes.
+ *
+ * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY.
+ */
 sealed class SceneKey(
     private val loggingName: String,
 ) {
@@ -26,6 +30,9 @@
      */
     object Bouncer : SceneKey("bouncer")
 
+    /** The communal scene shows the glanceable hub when device is locked and docked. */
+    object Communal : SceneKey("communal")
+
     /**
      * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
      * content from the scene framework.
@@ -35,14 +42,14 @@
     /** The lockscreen is the scene that shows when the device is locked. */
     object Lockscreen : SceneKey("lockscreen")
 
+    /** The quick settings scene shows the quick setting tiles. */
+    object QuickSettings : SceneKey("quick_settings")
+
     /**
      * The shade is the scene whose primary purpose is to show a scrollable list of notifications.
      */
     object Shade : SceneKey("shade")
 
-    /** The quick settings scene shows the quick setting tiles. */
-    object QuickSettings : SceneKey("quick_settings")
-
     override fun toString(): String {
         return loggingName
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index acb6d96..7a0c087 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -151,10 +151,9 @@
 
         return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
                 ? new ScreenRecordPermissionDialog(context,  getHostUserHandle(), this,
-                    activityStarter, dialogLaunchAnimator, mUserContextProvider,
-                    onStartRecordingClicked)
-                : new ScreenRecordDialog(context, this, activityStarter,
-                mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
+                    activityStarter, mUserContextProvider, onStartRecordingClicked)
+                : new ScreenRecordDialog(context, this, mUserContextProvider,
+                        onStartRecordingClicked);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 2a21aaa..91e3b60 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,6 @@
 
 import static android.app.Activity.RESULT_OK;
 
-import static com.android.systemui.media.MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER;
 import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
@@ -28,13 +27,11 @@
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ResultReceiver;
 import android.view.Gravity;
-import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.ArrayAdapter;
@@ -45,13 +42,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.media.MediaProjectionAppSelectorActivity;
 import com.android.systemui.media.MediaProjectionCaptureTarget;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
@@ -71,23 +62,15 @@
     private final UserContextProvider mUserContextProvider;
     @Nullable
     private final Runnable mOnStartRecordingClicked;
-    private final ActivityStarter mActivityStarter;
-    private final FeatureFlags mFlags;
-    private final DialogLaunchAnimator mDialogLaunchAnimator;
     private Switch mTapsSwitch;
     private Switch mAudioSwitch;
     private Spinner mOptions;
 
     public ScreenRecordDialog(Context context, RecordingController controller,
-            ActivityStarter activityStarter, UserContextProvider userContextProvider,
-            FeatureFlags flags, DialogLaunchAnimator dialogLaunchAnimator,
-            @Nullable Runnable onStartRecordingClicked) {
+            UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
         super(context);
         mController = controller;
         mUserContextProvider = userContextProvider;
-        mActivityStarter = activityStarter;
-        mDialogLaunchAnimator = dialogLaunchAnimator;
-        mFlags = flags;
         mOnStartRecordingClicked = onStartRecordingClicked;
     }
 
@@ -120,31 +103,6 @@
             dismiss();
         });
 
-        if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
-            TextView appBtn = findViewById(R.id.button_app);
-
-            appBtn.setVisibility(View.VISIBLE);
-            appBtn.setOnClickListener(v -> {
-                Intent intent = new Intent(getContext(), MediaProjectionAppSelectorActivity.class);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-                // We can't start activity for result here so we use result receiver to get
-                // the selected target to capture
-                intent.putExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
-                        new CaptureTargetResultReceiver());
-
-                ActivityLaunchAnimator.Controller animationController =
-                        mDialogLaunchAnimator.createActivityLaunchController(appBtn);
-
-                if (animationController == null) {
-                    dismiss();
-                }
-
-                mActivityStarter.startActivity(intent, /* dismissShade= */ true,
-                        animationController);
-            });
-        }
-
         mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
         mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
         mOptions = findViewById(R.id.screen_recording_options);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 9c5da10..56d732e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -33,7 +33,6 @@
 import android.widget.Switch
 import androidx.annotation.LayoutRes
 import com.android.systemui.R
-import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.media.MediaProjectionAppSelectorActivity
 import com.android.systemui.media.MediaProjectionCaptureTarget
 import com.android.systemui.plugins.ActivityStarter
@@ -45,7 +44,6 @@
     private val hostUserHandle: UserHandle,
     private val controller: RecordingController,
     private val activityStarter: ActivityStarter,
-    private val dialogLaunchAnimator: DialogLaunchAnimator,
     private val userContextProvider: UserContextProvider,
     private val onStartRecordingClicked: Runnable?
 ) :
@@ -85,12 +83,7 @@
                     MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
                     hostUserHandle
                 )
-
-                val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
-                if (animationController == null) {
-                    dismiss()
-                }
-                activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
+                activityStarter.startActivity(intent, /* dismissShade= */ true)
             }
             dismiss()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index c5bc2fb..03c3f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -150,16 +150,18 @@
     }
 
     /**
-     * Returns a [RequestCallback] that calls [RequestCallback.onFinish] only when all callbacks for
-     * id created have finished.
+     * Returns a [RequestCallback] that wraps [originalCallback].
      *
-     * If any callback created calls [reportError], then following [onFinish] are not considered.
+     * Each [RequestCallback] created with [createCallbackForId] is expected to be used with either
+     * [reportError] or [onFinish]. Once they are both called:
+     * - If any finished with an error, [reportError] of [originalCallback] is called
+     * - Otherwise, [onFinish] is called.
      */
     private class MultiResultCallbackWrapper(
         private val originalCallback: RequestCallback,
     ) {
         private val idsPending = mutableSetOf<Int>()
-        private var errorReported = false
+        private val idsWithErrors = mutableSetOf<Int>()
 
         /**
          * Creates a callback for [id].
@@ -172,23 +174,32 @@
             idsPending += id
             return object : RequestCallback {
                 override fun reportError() {
-                    Log.d(TAG, "ReportError id=$id")
-                    Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
-                    Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "reportError id=$id")
-                    originalCallback.reportError()
-                    errorReported = true
+                    endTrace("reportError id=$id")
+                    idsWithErrors += id
+                    idsPending -= id
+                    reportToOriginalIfNeeded()
                 }
 
                 override fun onFinish() {
-                    Log.d(TAG, "onFinish id=$id")
-                    if (errorReported) return
+                    endTrace("onFinish id=$id")
                     idsPending -= id
-                    Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "onFinish id=$id")
-                    if (idsPending.isEmpty()) {
-                        Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
-                        originalCallback.onFinish()
-                    }
+                    reportToOriginalIfNeeded()
                 }
+
+                private fun endTrace(reason: String) {
+                    Log.d(TAG, "Finished waiting for id=$id. $reason")
+                    Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
+                    Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, reason)
+                }
+            }
+        }
+
+        private fun reportToOriginalIfNeeded() {
+            if (idsPending.isNotEmpty()) return
+            if (idsWithErrors.isEmpty()) {
+                originalCallback.onFinish()
+            } else {
+                originalCallback.reportError()
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 1e8542f..32df5121b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -99,7 +99,11 @@
 
     /** Informs about coarse grained state of the Controller. */
     public interface RequestCallback {
-        /** Respond to the current request indicating the screenshot request failed. */
+        /**
+         * Respond to the current request indicating the screenshot request failed.
+         * <p>
+         * After this, the service will be disconnected and all visible UI is removed.
+         */
         void reportError();
 
         /** The controller has completed handling this request UI has been removed */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 93a3e90..bd592c9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.pm.UserInfo
 import android.os.UserHandle
-import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 
 /**
@@ -68,8 +67,8 @@
     interface Callback {
 
         /**
-         * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
-         * auto-decremented after the completion of this method.
+         * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
+         * called automatically after the completion of this method.
          */
         fun onUserChanging(newUser: Int, userContext: Context) {}
 
@@ -78,12 +77,12 @@
          * Override this method to run things while the screen is frozen for the user switch.
          * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
          * screen further. Please be aware that code executed in this callback will lengthen the
-         * user switch duration. When overriding this method, countDown() MUST be called on the
-         * latch once execution is complete.
+         * user switch duration. When overriding this method, resultCallback#run() MUST be called
+         * once the  execution is complete.
          */
-        fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
+        fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) {
             onUserChanging(newUser, userContext)
-            latch.countDown()
+            resultCallback.run()
         }
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 5fb3c01..2f87301 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -33,11 +33,22 @@
 import androidx.annotation.WorkerThread
 import com.android.systemui.Dumpable
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
 import com.android.systemui.util.Assert
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
 import java.io.PrintWriter
 import java.lang.ref.WeakReference
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
+import javax.inject.Provider
 import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
 
@@ -58,20 +69,26 @@
  */
 open class UserTrackerImpl internal constructor(
     private val context: Context,
+    private val featureFlagsProvider: Provider<FeatureFlagsClassic>,
     private val userManager: UserManager,
     private val iActivityManager: IActivityManager,
     private val dumpManager: DumpManager,
-    private val backgroundHandler: Handler
+    private val appScope: CoroutineScope,
+    private val backgroundContext: CoroutineDispatcher,
+    private val backgroundHandler: Handler,
 ) : UserTracker, Dumpable, BroadcastReceiver() {
 
     companion object {
         private const val TAG = "UserTrackerImpl"
+        private const val USER_CHANGE_THRESHOLD = 5L * 1000 // 5 sec
     }
 
     var initialized = false
         private set
 
     private val mutex = Any()
+    private val isBackgroundUserSwitchEnabled: Boolean get() =
+        featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
 
     override var userId: Int by SynchronizedDelegate(context.userId)
         protected set
@@ -103,6 +120,10 @@
     @GuardedBy("callbacks")
     private val callbacks: MutableList<DataItem> = ArrayList()
 
+    private var beforeUserSwitchingJob: Job? = null
+    private var userSwitchingJob: Job? = null
+    private var afterUserSwitchingJob: Job? = null
+
     open fun initialize(startingUser: Int) {
         if (initialized) {
             return
@@ -119,7 +140,7 @@
             addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
         }
-        context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+        context.registerReceiverForAllUsers(this, filter, null, backgroundHandler)
 
         registerUserSwitchObserver()
 
@@ -162,16 +183,39 @@
     private fun registerUserSwitchObserver() {
         iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
             override fun onBeforeUserSwitching(newUserId: Int) {
-                handleBeforeUserSwitching(newUserId)
+                if (isBackgroundUserSwitchEnabled) {
+                    beforeUserSwitchingJob?.cancel()
+                    beforeUserSwitchingJob = appScope.launch(backgroundContext) {
+                        handleBeforeUserSwitching(newUserId)
+                    }
+                } else {
+                    handleBeforeUserSwitching(newUserId)
+                }
             }
 
             override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
-                handleUserSwitching(newUserId)
-                reply?.sendResult(null)
+                if (isBackgroundUserSwitchEnabled) {
+                    userSwitchingJob?.cancel()
+                    userSwitchingJob = appScope.launch(backgroundContext) {
+                        handleUserSwitchingCoroutines(newUserId) {
+                            reply?.sendResult(null)
+                        }
+                    }
+                } else {
+                    handleUserSwitching(newUserId)
+                    reply?.sendResult(null)
+                }
             }
 
             override fun onUserSwitchComplete(newUserId: Int) {
-                handleUserSwitchComplete(newUserId)
+                if (isBackgroundUserSwitchEnabled) {
+                    afterUserSwitchingJob?.cancel()
+                    afterUserSwitchingJob = appScope.launch(backgroundContext) {
+                        handleUserSwitchComplete(newUserId)
+                    }
+                } else {
+                    handleUserSwitchComplete(newUserId)
+                }
             }
         }, TAG)
     }
@@ -195,7 +239,7 @@
             val callback = it.callback.get()
             if (callback != null) {
                 it.executor.execute {
-                    callback.onUserChanging(userId, userContext, latch)
+                    callback.onUserChanging(userId, userContext) { latch.countDown() }
                 }
             } else {
                 latch.countDown()
@@ -205,6 +249,28 @@
     }
 
     @WorkerThread
+    protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) =
+            coroutineScope {
+                Assert.isNotMainThread()
+                Log.i(TAG, "Switching to user $newUserId")
+
+                for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
+                    val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
+                    launch(callbackDataItem.executor.asCoroutineDispatcher()) {
+                        val mutex = Mutex(true)
+                        val thresholdLogJob = launch(backgroundContext) {
+                            delay(USER_CHANGE_THRESHOLD)
+                            Log.e(TAG, "Failed to finish $callback in time")
+                        }
+                        callback.onUserChanging(userId, userContext) { mutex.unlock() }
+                        mutex.lock()
+                        thresholdLogJob.cancel()
+                    }.join()
+                }
+                onDone()
+            }
+
+    @WorkerThread
     protected open fun handleUserSwitchComplete(newUserId: Int) {
         Assert.isNotMainThread()
         Log.i(TAG, "Switched to user $newUserId")
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index e9a1dd7..a0dd924 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -25,8 +25,10 @@
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.DisplayTrackerImpl;
 import com.android.systemui.settings.UserContentResolverProvider;
@@ -42,6 +44,11 @@
 import dagger.multibindings.ClassKey;
 import dagger.multibindings.IntoMap;
 
+import javax.inject.Provider;
+
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
+
 /**
  * Dagger Module for classes found within the com.android.systemui.settings package.
  */
@@ -60,14 +67,17 @@
     @Provides
     static UserTracker provideUserTracker(
             Context context,
+            Provider<FeatureFlagsClassic> featureFlagsProvider,
             UserManager userManager,
             IActivityManager iActivityManager,
             DumpManager dumpManager,
+            @Application CoroutineScope appScope,
+            @Background CoroutineDispatcher backgroundDispatcher,
             @Background Handler handler
     ) {
         int startingUser = ActivityManager.getCurrentUser();
-        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
-                dumpManager, handler);
+        UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
+                iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
         tracker.initialize(startingUser);
         return tracker;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 49ce832..92ccdb5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -141,7 +141,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.shared.model.WakefulnessModel;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
-import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -206,7 +205,6 @@
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
@@ -223,10 +221,10 @@
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.util.Compile;
-import com.android.systemui.util.LargeScreenUtils;
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -236,7 +234,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.Optional;
 import java.util.function.Consumer;
 
@@ -320,7 +317,6 @@
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     private final LayoutInflater mLayoutInflater;
     private final FeatureFlags mFeatureFlags;
-    private final PowerManager mPowerManager;
     private final AccessibilityManager mAccessibilityManager;
     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
     private final PulseExpansionHandler mPulseExpansionHandler;
@@ -357,7 +353,6 @@
     private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate();
     private final NotificationGutsManager mGutsManager;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
-    private final KeyguardRootView mKeyguardRootView;
     private final QuickSettingsController mQsController;
     private final TouchHandler mTouchHandler = new TouchHandler();
 
@@ -370,8 +365,6 @@
     private float mExpandedHeight = 0;
     /** The current squish amount for the predictive back animation */
     private float mCurrentBackProgress = 0.0f;
-    private boolean mTracking;
-    private boolean mHintAnimationRunning;
     @Deprecated
     private KeyguardBottomAreaView mKeyguardBottomArea;
     private boolean mExpanding;
@@ -387,7 +380,6 @@
     private int mMaxAllowedKeyguardNotifications;
     private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
-    private KeyguardStatusBarView mKeyguardStatusBar;
     private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
     private KeyguardStatusViewController mKeyguardStatusViewController;
     private final LockIconViewController mLockIconViewController;
@@ -594,6 +586,8 @@
     private boolean mGestureWaitForTouchSlop;
     private boolean mIgnoreXTouchSlop;
     private boolean mExpandLatencyTracking;
+    private boolean mUseExternalTouch = false;
+
     /**
      * Whether we're waking up and will play the delayed doze animation in
      * {@link NotificationWakeUpCoordinator}. If so, we'll want to keep the clock centered until the
@@ -622,7 +616,7 @@
     private int mLockscreenToDreamingTransitionTranslationY;
     private int mGoneToDreamingTransitionTranslationY;
     private int mLockscreenToOccludedTransitionTranslationY;
-
+    private SplitShadeStateController mSplitShadeStateController;
     private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
             mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
     private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable =
@@ -781,7 +775,7 @@
             SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
             KeyguardViewConfigurator keyguardViewConfigurator,
             KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
-            KeyguardRootView keyguardRootView) {
+            SplitShadeStateController splitShadeStateController) {
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
             public void onKeyguardFadingAwayChanged() {
@@ -877,8 +871,9 @@
         mFragmentService = fragmentService;
         mStatusBarService = statusBarService;
         mSettingsChangeObserver = new SettingsChangeObserver(handler);
+        mSplitShadeStateController = splitShadeStateController;
         mSplitShadeEnabled =
-                LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
+                mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
         mView.setWillNotDraw(!DEBUG_DRAWABLE);
         mShadeHeaderController = shadeHeaderController;
         mLayoutInflater = layoutInflater;
@@ -886,7 +881,6 @@
         mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
         mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
         mFalsingCollector = falsingCollector;
-        mPowerManager = powerManager;
         mWakeUpCoordinator = coordinator;
         mMainDispatcher = mainDispatcher;
         mAccessibilityManager = accessibilityManager;
@@ -987,7 +981,6 @@
                     }
                 });
         mAlternateBouncerInteractor = alternateBouncerInteractor;
-        mKeyguardRootView = keyguardRootView;
         dumpManager.registerDumpable(this);
     }
 
@@ -1006,7 +999,7 @@
         // cause blurring. This will eventually be re-enabled by the panel view on
         // ACTION_UP, since the user's finger might still be down after a swipe to
         // unlock gesture, and we don't want that to cause blurring either.
-        mDepthController.setBlursDisabledForUnlock(mTracking);
+        mDepthController.setBlursDisabledForUnlock(isTracking());
 
         if (playingCannedAnimation && !isWakeAndUnlockNotFromDream) {
             // Hide the panel so it's not in the way or the surface behind the
@@ -1042,7 +1035,6 @@
     @VisibleForTesting
     void onFinishInflate() {
         loadDimens();
-        mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
 
         FrameLayout userAvatarContainer = null;
         KeyguardUserSwitcherView keyguardUserSwitcherView = null;
@@ -1060,7 +1052,7 @@
 
         mKeyguardStatusBarViewController =
                 mKeyguardStatusBarViewComponentFactory.build(
-                                mKeyguardStatusBar,
+                                mView.findViewById(R.id.keyguard_header),
                                 mShadeViewStateProvider)
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
@@ -1233,7 +1225,7 @@
     private void updateViewControllers(
             FrameLayout userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView) {
-        updateStatusBarViewController();
+        updateStatusViewController();
         if (mKeyguardUserSwitcherController != null) {
             // Try to close the switcher so that callbacks are triggered if necessary.
             // Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1264,7 +1256,7 @@
     }
 
     /** Updates the StatusBarViewController and updates any that depend on it. */
-    public void updateStatusBarViewController() {
+    public void updateStatusViewController() {
         // Re-associate the KeyguardStatusViewController
         if (mKeyguardStatusViewController != null) {
             mKeyguardStatusViewController.onDestroy();
@@ -1300,7 +1292,7 @@
     @Override
     public void updateResources() {
         final boolean newSplitShadeEnabled =
-                LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
+                mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
         final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
         mSplitShadeEnabled = newSplitShadeEnabled;
         mQsController.updateResources();
@@ -1414,13 +1406,6 @@
 
         updateViewControllers(userAvatarView, keyguardUserSwitcherView);
 
-        if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) && !mFeatureFlags.isEnabled(
-                Flags.LAZY_INFLATE_KEYGUARD)) {
-            attachSplitShadeMediaPlayerContainer(
-                    mKeyguardViewConfigurator.getKeyguardRootView()
-                        .findViewById(R.id.status_view_media_container));
-        }
-
         if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
             // Update keyguard bottom area
             int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1519,7 +1504,7 @@
     }
 
     private boolean shouldAvoidChangingNotificationsCount() {
-        return mHintAnimationRunning || mUnlockedScreenOffAnimationController.isAnimationPlaying();
+        return mUnlockedScreenOffAnimationController.isAnimationPlaying();
     }
 
     @Deprecated
@@ -1690,6 +1675,10 @@
 
     @ClockSize
     private int computeDesiredClockSize() {
+        if (shouldForceSmallClock()) {
+            return SMALL;
+        }
+
         if (mSplitShadeEnabled) {
             return computeDesiredClockSizeForSplitShade();
         }
@@ -1722,6 +1711,13 @@
         return LARGE;
     }
 
+    private boolean shouldForceSmallClock() {
+        return mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)
+                && !isOnAod()
+                // True on small landscape screens
+                && mResources.getBoolean(R.bool.force_small_clock_on_lockscreen);
+    }
+
     private void updateKeyguardStatusViewAlignment(boolean animate) {
         boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
         ConstraintLayout layout;
@@ -1849,6 +1845,9 @@
     /** Returns extra space available to show the shelf on lockscreen */
     @VisibleForTesting
     float getVerticalSpaceForLockscreenShelf() {
+        if (mSplitShadeEnabled) {
+            return 0f;
+        }
         final float lockIconPadding = getLockIconPadding();
 
         final float noShelfOverlapBottomPadding =
@@ -2253,7 +2252,10 @@
     }
 
     @Override
-    public void startWaitingForExpandGesture() {
+    public void startInputFocusTransfer() {
+        if (!mCommandQueue.panelsEnabled()) {
+            return;
+        }
         if (!isFullyCollapsed()) {
             return;
         }
@@ -2263,16 +2265,36 @@
     }
 
     @Override
-    public void stopWaitingForExpandGesture(boolean cancel, final float velocity) {
+    public void cancelInputFocusTransfer() {
+        if (!mCommandQueue.panelsEnabled()) {
+            return;
+        }
         if (mExpectingSynthesizedDown) {
             mExpectingSynthesizedDown = false;
-            if (cancel) {
-                collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-            } else {
-                // Window never will receive touch events that typically trigger haptic on open.
-                maybeVibrateOnOpening(false /* openingWithTouch */);
-                fling(velocity > 1f ? 1000f * velocity : 0  /* expand */);
-            }
+            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+            onTrackingStopped(false);
+        }
+    }
+
+    /**
+     * There are two scenarios behind this function call. First, input focus transfer has
+     * successfully happened and this view already received synthetic DOWN event.
+     * (mExpectingSynthesizedDown == false). Do nothing.
+     *
+     * Second, before input focus transfer finished, user may have lifted finger in previous window
+     * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
+     * this case, we use the velocity to trigger fling event.
+     */
+    @Override
+    public void finishInputFocusTransfer(final float velocity) {
+        if (!mCommandQueue.panelsEnabled()) {
+            return;
+        }
+        if (mExpectingSynthesizedDown) {
+            mExpectingSynthesizedDown = false;
+            // Window never will receive touch events that typically trigger haptic on open.
+            maybeVibrateOnOpening(false /* openingWithTouch */);
+            fling(velocity > 1f ? 1000f * velocity : 0  /* expand */);
             onTrackingStopped(false);
         }
     }
@@ -2549,10 +2571,10 @@
     private void onHeightUpdated(float expandedHeight) {
         if (expandedHeight <= 0) {
             mShadeLog.logExpansionChanged("onHeightUpdated: fully collapsed.",
-                    mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+                    mExpandedFraction, isExpanded(), isTracking(), mExpansionDragDownAmountPx);
         } else if (isFullyExpanded()) {
             mShadeLog.logExpansionChanged("onHeightUpdated: fully expanded.",
-                    mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+                    mExpandedFraction, isExpanded(), isTracking(), mExpansionDragDownAmountPx);
         }
         if (!mQsController.getExpanded() || mQsController.isExpandImmediate()
                 || mIsExpandingOrCollapsing && mQsController.getExpandedWhenExpandingStarted()) {
@@ -2611,6 +2633,7 @@
         if (mPanelExpanded != isExpanded) {
             mPanelExpanded = isExpanded;
             updateSystemUiStateFlags();
+            mShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(mPanelExpanded);
             mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
             if (!isExpanded) {
                 mQsController.closeQsCustomizer();
@@ -2645,7 +2668,7 @@
                 && !mHeadsUpManager.hasPinnedHeadsUp()) {
             alpha = getFadeoutAlpha();
         }
-        if (mBarState == KEYGUARD && !mHintAnimationRunning
+        if (mBarState == KEYGUARD
                 && !mKeyguardBypassController.getBypassEnabled()
                 && !mQsController.getFullyExpanded()) {
             alpha *= mClockPositionResult.clockAlpha;
@@ -2683,7 +2706,7 @@
         //   change due to "unlock hint animation." In this case, fading out the bottom area
         //   would also hide the message that says "swipe to unlock," we don't want to do that.
         float expansionAlpha = MathUtils.constrainedMap(0f, 1f,
-                isUnlockHintRunning() ? 0f : KeyguardBouncerConstants.ALPHA_EXPANSION_THRESHOLD, 1f,
+                KeyguardBouncerConstants.ALPHA_EXPANSION_THRESHOLD, 1f,
                 getExpandedFraction());
 
         float alpha = Math.min(expansionAlpha, 1 - mQsController.computeExpansionFraction());
@@ -2745,7 +2768,7 @@
             mAnimateAfterExpanding = animate;
             mUpdateFlingOnLayout = false;
             abortAnimations();
-            if (mTracking) {
+            if (isTracking()) {
                 // The panel is expanded after this call.
                 onTrackingStopped(true /* expands */);
             }
@@ -2832,7 +2855,7 @@
 
     private void onTrackingStarted() {
         endClosing();
-        mTracking = true;
+        mShadeRepository.setLegacyShadeTracking(true);
         mTrackingStartedListener.onTrackingStarted();
         notifyExpandingStarted();
         updateExpansionAndVisibility();
@@ -2846,7 +2869,7 @@
     }
 
     private void onTrackingStopped(boolean expand) {
-        mTracking = false;
+        mShadeRepository.setLegacyShadeTracking(false);
 
         updateExpansionAndVisibility();
         if (expand) {
@@ -2865,44 +2888,6 @@
                 mView.getHeight(), mNavigationBarBottomHeight);
     }
 
-    @VisibleForTesting
-    void startUnlockHintAnimation() {
-        if (mPowerManager.isPowerSaveMode() || mAmbientState.getDozeAmount() > 0f) {
-            onUnlockHintStarted();
-            onUnlockHintFinished();
-            return;
-        }
-
-        // We don't need to hint the user if an animation is already running or the user is changing
-        // the expansion.
-        if (mHeightAnimator != null || mTracking) {
-            return;
-        }
-        notifyExpandingStarted();
-        startUnlockHintAnimationPhase1(() -> {
-            notifyExpandingFinished();
-            onUnlockHintFinished();
-            mHintAnimationRunning = false;
-        });
-        onUnlockHintStarted();
-        mHintAnimationRunning = true;
-    }
-
-    @VisibleForTesting
-    void onUnlockHintFinished() {
-        // Delay the reset a bit so the user can read the text.
-        mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
-        mScrimController.setExpansionAffectsAlpha(true);
-        mNotificationStackScrollLayoutController.setUnlockHintRunning(false);
-    }
-
-    @VisibleForTesting
-    void onUnlockHintStarted() {
-        mKeyguardIndicationController.showActionToUnlock();
-        mScrimController.setExpansionAffectsAlpha(false);
-        mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
-    }
-
     private boolean shouldUseDismissingAnimation() {
         return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
                 || !isTracking());
@@ -2989,7 +2974,7 @@
                                 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
                         mLockscreenGestureLogger
                                 .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
-                        startUnlockHintAnimation();
+                        mKeyguardIndicationController.showActionToUnlock();
                     }
                 }
                 break;
@@ -3056,7 +3041,7 @@
     }
 
     private void updateExpandedHeight(float expandedHeight) {
-        if (mTracking) {
+        if (isTracking()) {
             mNotificationStackScrollLayoutController
                     .setExpandingVelocity(getCurrentExpandVelocity());
         }
@@ -3145,7 +3130,7 @@
         mTouchDisabled = disabled;
         if (mTouchDisabled) {
             cancelHeightAnimator();
-            if (mTracking) {
+            if (isTracking()) {
                 onTrackingStopped(true /* expanded */);
             }
             notifyExpandingFinished();
@@ -3384,7 +3369,7 @@
 
     @Override
     public void blockExpansionForCurrentTouch() {
-        mBlockingExpansionForCurrentTouch = mTracking;
+        mBlockingExpansionForCurrentTouch = isTracking();
     }
 
     @Override
@@ -3398,8 +3383,7 @@
         ipw.print("mIsLaunchAnimationRunning="); ipw.println(mIsLaunchAnimationRunning);
         ipw.print("mOverExpansion="); ipw.println(mOverExpansion);
         ipw.print("mExpandedHeight="); ipw.println(mExpandedHeight);
-        ipw.print("mTracking="); ipw.println(mTracking);
-        ipw.print("mHintAnimationRunning="); ipw.println(mHintAnimationRunning);
+        ipw.print("isTracking()="); ipw.println(isTracking());
         ipw.print("mExpanding="); ipw.println(mExpanding);
         ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled);
         ipw.print("mKeyguardNotificationBottomPadding=");
@@ -3737,7 +3721,7 @@
             mQsController.beginJankMonitoring(isFullyCollapsed());
         }
         mInitialOffsetOnTouch = expandedHeight;
-        if (!mTracking || isFullyCollapsed()) {
+        if (!isTracking() || isFullyCollapsed()) {
             mInitialExpandY = newY;
             mInitialExpandX = newX;
         } else {
@@ -3755,7 +3739,7 @@
         mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
         mTrackingPointer = -1;
         mAmbientState.setSwipingUp(false);
-        if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
+        if ((isTracking() && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
                 || Math.abs(y - mInitialExpandY) > mTouchSlop
                 || (!isFullyExpanded() && !isFullyCollapsed())
                 || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
@@ -3776,8 +3760,6 @@
                     expand = true;
                     mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
                             forceCancel, expand);
-                } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
-                    expand = false;
                 } else {
                     // If we get a cancel, put the shade back to the state it was in when the
                     // gesture started
@@ -3919,7 +3901,7 @@
             return;
         }
 
-        if (mTracking && !(mBlockingExpansionForCurrentTouch
+        if (isTracking() && !(mBlockingExpansionForCurrentTouch
                 || mQsController.isTrackingBlocked())) {
             return;
         }
@@ -3945,7 +3927,7 @@
             float maxPanelHeight = getMaxPanelTransitionDistance();
             if (mHeightAnimator == null) {
                 // Split shade has its own overscroll logic
-                if (mTracking) {
+                if (isTracking()) {
                     float overExpansionPixels = Math.max(0, h - maxPanelHeight);
                     setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
                 }
@@ -4032,12 +4014,12 @@
     }
 
     public boolean isTracking() {
-        return mTracking;
+        return mShadeRepository.getLegacyShadeTracking().getValue();
     }
 
     @Override
     public boolean canBeCollapsed() {
-        return !isFullyCollapsed() && !mTracking && !mClosing;
+        return !isFullyCollapsed() && !isTracking() && !mClosing;
     }
 
     @Override
@@ -4058,73 +4040,6 @@
         mView.removeCallbacks(mFlingCollapseRunnable);
     }
 
-    @Override
-    public boolean isUnlockHintRunning() {
-        return mHintAnimationRunning;
-    }
-
-    /**
-     * Phase 1: Move everything upwards.
-     */
-    private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
-        float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
-        ValueAnimator animator = createHeightAnimator(target);
-        animator.setDuration(250);
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        animator.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mCancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (mCancelled) {
-                    setAnimator(null);
-                    onAnimationFinished.run();
-                } else {
-                    startUnlockHintAnimationPhase2(onAnimationFinished);
-                }
-            }
-        });
-        animator.start();
-        setAnimator(animator);
-
-
-        if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            final ViewPropertyAnimator mKeyguardRootViewAnimator = mKeyguardRootView.animate();
-            mKeyguardRootViewAnimator
-                    .translationY(-mHintDistance)
-                    .setDuration(250)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .withEndAction(() -> mKeyguardRootViewAnimator
-                            .translationY(0)
-                            .setDuration(450)
-                            .setInterpolator(mBounceInterpolator)
-                            .start())
-                    .start();
-        } else {
-            final List<ViewPropertyAnimator> indicationAnimators =
-                    mKeyguardBottomArea.getIndicationAreaAnimators();
-
-            for (final ViewPropertyAnimator indicationAreaAnimator : indicationAnimators) {
-                indicationAreaAnimator
-                    .translationY(-mHintDistance)
-                    .setDuration(250)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .withEndAction(() -> indicationAreaAnimator
-                        .translationY(0)
-                        .setDuration(450)
-                        .setInterpolator(mBounceInterpolator)
-                        .start())
-                    .start();
-            }
-        }
-
-    }
-
     private void setAnimator(ValueAnimator animator) {
         mHeightAnimator = animator;
         if (animator == null && mPanelUpdateWhenAnimatorEnds) {
@@ -4135,7 +4050,7 @@
 
     /** Returns whether a shade or QS expansion animation is running */
     private boolean isShadeOrQsHeightAnimationRunning() {
-        return mHeightAnimator != null && !mHintAnimationRunning && !mIsSpringBackAnimation;
+        return mHeightAnimator != null && !mIsSpringBackAnimation;
     }
 
     /**
@@ -4196,7 +4111,7 @@
     @Override
     public void updateExpansionAndVisibility() {
         mShadeExpansionStateManager.onPanelExpansionChanged(
-                mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+                mExpandedFraction, isExpanded(), isTracking(), mExpansionDragDownAmountPx);
 
         updateVisibility();
     }
@@ -4206,7 +4121,7 @@
         return mExpandedFraction > 0f
                 || mInstantExpanding
                 || isPanelVisibleBecauseOfHeadsUp()
-                || mTracking
+                || isTracking()
                 || mHeightAnimator != null
                 || isPanelVisibleBecauseScrimIsAnimatingOff()
                 && !mIsSpringBackAnimation;
@@ -4214,9 +4129,7 @@
 
     /** Called when the user performs a click anywhere in the empty area of the panel. */
     private void onEmptySpaceClick() {
-        if (!mHintAnimationRunning)  {
-            onMiddleClicked();
-        }
+        onMiddleClicked();
     }
 
     @VisibleForTesting
@@ -4238,12 +4151,22 @@
 
     /** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */
     boolean handleExternalInterceptTouch(MotionEvent event) {
-        return mTouchHandler.onInterceptTouchEvent(event);
+        try {
+            mUseExternalTouch = true;
+            return mTouchHandler.onInterceptTouchEvent(event);
+        } finally {
+            mUseExternalTouch = false;
+        }
     }
 
     @Override
     public boolean handleExternalTouch(MotionEvent event) {
-        return mTouchHandler.onTouchEvent(event);
+        try {
+            mUseExternalTouch = true;
+            return mTouchHandler.onTouchEvent(event);
+        } finally {
+            mUseExternalTouch = false;
+        }
     }
 
     @Override
@@ -4816,11 +4739,6 @@
         return mStatusBarStateListener;
     }
 
-    @VisibleForTesting
-    boolean isHintAnimationRunning() {
-        return mHintAnimationRunning;
-    }
-
     private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) {
         if (state != WINDOW_STATE_SHOWING
                 && mStatusBarStateController.getState() == StatusBarState.SHADE) {
@@ -4835,9 +4753,20 @@
     public final class TouchHandler implements View.OnTouchListener, Gefingerpoken {
         private long mLastTouchDownTime = -1L;
 
-        /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
+        /**
+         * With the shade and lockscreen being separated in the view hierarchy, touch handling now
+         * originates with the parent window through {@link #handleExternalTouch}. This allows for
+         * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
+         * handling.
+         *
+         * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
+         */
         @Override
         public boolean onInterceptTouchEvent(MotionEvent event) {
+            if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+                return false;
+            }
+
             mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
             if (mQsController.disallowTouches()) {
                 mShadeLog.logMotionEvent(event,
@@ -4909,15 +4838,14 @@
                     mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
                     mMinExpandHeight = 0.0f;
                     mDownTime = mSystemClock.uptimeMillis();
-                    if (mAnimatingOnDown && mClosing && !mHintAnimationRunning) {
+                    if (mAnimatingOnDown && mClosing) {
                         cancelHeightAnimator();
                         mTouchSlopExceeded = true;
                         mShadeLog.v("NotificationPanelViewController MotionEvent intercepted:"
-                                + " mAnimatingOnDown: true, mClosing: true, mHintAnimationRunning:"
-                                + " false");
+                                + " mAnimatingOnDown: true, mClosing: true");
                         return true;
                     }
-                    if (!mTracking || isFullyCollapsed()) {
+                    if (!isTracking() || isFullyCollapsed()) {
                         mInitialExpandY = y;
                         mInitialExpandX = x;
                     } else {
@@ -4991,8 +4919,20 @@
             return onTouchEvent(event);
         }
 
+        /**
+         * With the shade and lockscreen being separated in the view hierarchy, touch handling now
+         * originates with the parent window through {@link #handleExternalTouch}. This allows for
+         * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
+         * handling.
+         *
+         * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
+         */
         @Override
         public boolean onTouchEvent(MotionEvent event) {
+            if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL) && !mUseExternalTouch) {
+                return false;
+            }
+
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 if (event.getDownTime() == mLastTouchDownTime) {
                     // An issue can occur when swiping down after unlock, where multiple down
@@ -5012,10 +4952,9 @@
                 return false;
             }
 
-            // Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
+            // Do not allow panel expansion if bouncer is scrimmed,
             // otherwise user would be able to pull down QS or expand the shade.
-            if (mCentralSurfaces.isBouncerShowingScrimmed()
-                    || mCentralSurfaces.isBouncerShowingOverDream()) {
+            if (mCentralSurfaces.isBouncerShowingScrimmed()) {
                 mShadeLog.logMotionEvent(event,
                         "onTouch: ignore touch, bouncer scrimmed or showing over dream");
                 return false;
@@ -5094,7 +5033,7 @@
 
             // If dragging should not expand the notifications shade, then return false.
             if (!mNotificationsDragEnabled) {
-                if (mTracking) {
+                if (isTracking()) {
                     // Turn off tracking if it's on or the shade can get stuck in the down position.
                     onTrackingStopped(true /* expand */);
                 }
@@ -5220,7 +5159,9 @@
                             && (Math.abs(h) > Math.abs(x - mInitialExpandX)
                             || mIgnoreXTouchSlop)) {
                         mTouchSlopExceeded = true;
-                        if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
+                        if (mGestureWaitForTouchSlop
+                                && !isTracking()
+                                && !mCollapsedAndHeadsUpOnDown) {
                             if (mInitialOffsetOnTouch != 0f) {
                                 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                                 h = 0;
@@ -5235,7 +5176,7 @@
                         mTouchAboveFalsingThreshold = true;
                         mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
                     }
-                    if ((!mGestureWaitForTouchSlop || mTracking)
+                    if ((!mGestureWaitForTouchSlop || isTracking())
                             && !(mBlockingExpansionForCurrentTouch
                             || mQsController.isTrackingBlocked())) {
                         // Count h==0 as part of swipe-up,
@@ -5261,7 +5202,7 @@
                     }
                     break;
             }
-            return !mGestureWaitForTouchSlop || mTracking;
+            return !mGestureWaitForTouchSlop || isTracking();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 880ba92..96fae14 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -267,6 +267,9 @@
         }
         mView.setLayoutInsetsController(mNotificationInsetsController);
         mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
+            boolean mUseDragDownHelperForTouch = false;
+            boolean mLastInterceptWasDragDownHelper = false;
+
             @Override
             public Boolean handleDispatchTouchEvent(MotionEvent ev) {
                 if (mStatusBarViewController == null) { // Fix for b/192490822
@@ -360,10 +363,8 @@
                 );
 
                 // In case we start outside of the view bounds (below the status bar), we need to
-                // dispatch
-                // the touch manually as the view system can't accommodate for touches outside of
-                // the
-                // regular view bounds.
+                // dispatch the touch manually as the view system can't accommodate for touches
+                // outside of the regular view bounds.
                 if (isDown && ev.getY() >= mView.getBottom()) {
                     mExpandingBelowNotch = true;
                     expandingBelowNotch = true;
@@ -405,6 +406,15 @@
 
             @Override
             public boolean shouldInterceptTouchEvent(MotionEvent ev) {
+                boolean intercepted = shouldInterceptTouchEventInternal(ev);
+                if (intercepted) {
+                    mUseDragDownHelperForTouch = mLastInterceptWasDragDownHelper;
+                }
+                return intercepted;
+            }
+
+            private boolean shouldInterceptTouchEventInternal(MotionEvent ev) {
+                mLastInterceptWasDragDownHelper = false;
                 if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
                         && !mDockManager.isDocked()) {
                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -431,19 +441,36 @@
                 }
 
                 if (mNotificationPanelViewController.isFullyExpanded()
-                        && mDragDownHelper.isDragDownEnabled()
                         && !mService.isBouncerShowing()
                         && !mStatusBarStateController.isDozing()) {
-                    boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
-                    if (result) {
-                        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                            mShadeLogger.d("NSWVC: drag down helper intercepted");
+                    if (mDragDownHelper.isDragDownEnabled()) {
+                        // This handles drag down over lockscreen
+                        boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
+                        if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+                            if (result) {
+                                mLastInterceptWasDragDownHelper = true;
+                                if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+                                    mShadeLogger.d("NSWVC: drag down helper intercepted");
+                                }
+                            } else if (didNotificationPanelInterceptEvent(ev)) {
+                                return true;
+                            }
+                        } else {
+                            if (result) {
+                                if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+                                    mShadeLogger.d("NSWVC: drag down helper intercepted");
+                                }
+                            }
+                        }
+                        return result;
+                    } else {
+                        // This else handles interactions on the full shade while unlocked
+                        if (didNotificationPanelInterceptEvent(ev)) {
+                            return true;
                         }
                     }
-                    return result;
-                } else {
-                    return false;
                 }
+                return false;
             }
 
             @Override
@@ -451,7 +478,9 @@
                 MotionEvent cancellation = MotionEvent.obtain(ev);
                 cancellation.setAction(MotionEvent.ACTION_CANCEL);
                 mStackScrollLayout.onInterceptTouchEvent(cancellation);
-                mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
+                if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+                    mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
+                }
                 cancellation.recycle();
             }
 
@@ -461,18 +490,27 @@
                 if (mStatusBarStateController.isDozing()) {
                     handled = !mDozeServiceHost.isPulsing();
                 }
-
                 if (mStatusBarKeyguardViewManager.onTouch(ev)) {
                     return true;
                 }
-
-                if (mDragDownHelper.isDragDownEnabled()
-                        || mDragDownHelper.isDraggingDown()) {
-                    // we still want to finish our drag down gesture when locking the screen
-                    return mDragDownHelper.onTouchEvent(ev) || handled;
+                if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+                    if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
+                        // we still want to finish our drag down gesture when locking the screen
+                        handled |= mDragDownHelper.onTouchEvent(ev) || handled;
+                    }
+                    if (!handled && mNotificationPanelViewController.handleExternalTouch(ev)) {
+                        return true;
+                    }
                 } else {
-                    return handled;
+                    if (mDragDownHelper.isDragDownEnabled()
+                            || mDragDownHelper.isDraggingDown()) {
+                        // we still want to finish our drag down gesture when locking the screen
+                        return mDragDownHelper.onTouchEvent(ev) || handled;
+                    } else {
+                        return handled;
+                    }
                 }
+                return handled;
             }
 
             @Override
@@ -520,6 +558,20 @@
         mDepthController.onPanelExpansionChanged(currentState);
     }
 
+    private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
+        if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+            // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need
+            // to also ask NotificationPanelViewController directly, in order to process swipe up
+            // events originating from notifications
+            if (mNotificationPanelViewController.handleExternalInterceptTouch(ev)) {
+                mShadeLogger.d("NSWVC: NPVC intercepted");
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     public NotificationShadeWindowView getView() {
         return mView;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 9412542..3873ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
 import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.LargeScreenUtils
 import com.android.systemui.util.ViewController
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -61,6 +62,7 @@
         private val featureFlags: FeatureFlags,
         private val
             notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
+        private val splitShadeStateController: SplitShadeStateController
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
     private var qsExpanded = false
@@ -127,6 +129,9 @@
 
         mView.setStackScroller(notificationStackScrollLayoutController.getView())
         mView.setMigratingNSSL(featureFlags.isEnabled(Flags.MIGRATE_NSSL))
+        if (featureFlags.isEnabled(Flags.QS_CONTAINER_GRAPH_OPTIMIZER)){
+            mView.enableGraphOptimization()
+        }
     }
 
     public override fun onViewAttached() {
@@ -149,7 +154,8 @@
     }
 
     fun updateResources() {
-        val newSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
+        val newSplitShadeEnabled =
+                splitShadeStateController.shouldUseSplitNotificationShade(resources)
         val splitShadeEnabledChanged = newSplitShadeEnabled != splitShadeEnabled
         splitShadeEnabled = newSplitShadeEnabled
         largeScreenShadeHeaderActive = LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index a4e439b..292cf8e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.shade;
 
+import static androidx.constraintlayout.core.widgets.Optimizer.OPTIMIZATION_GRAPH;
+
 import android.app.Fragment;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -24,7 +26,6 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
 import android.view.WindowInsets;
 
 import androidx.annotation.Nullable;
@@ -183,6 +184,10 @@
         mIsMigratingNSSL = isMigrating;
     }
 
+    void enableGraphOptimization() {
+        setOptimizationLevel(getOptimizationLevel() | OPTIMIZATION_GRAPH);
+    }
+
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         return TouchLogger.logDispatchTouch("NotificationsQuickSettingsContainer", ev,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index b2bbffd..d653cb4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -70,6 +70,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
@@ -100,6 +101,7 @@
 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.LargeScreenUtils;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
@@ -150,6 +152,7 @@
     private final ShadeLogger mShadeLog;
     private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
     private final CastController mCastController;
+    private final SplitShadeStateController mSplitShadeStateController;
     private final FeatureFlags mFeatureFlags;
     private final InteractionJankMonitor mInteractionJankMonitor;
     private final ShadeRepository mShadeRepository;
@@ -199,8 +202,6 @@
     private float mInitialTouchY;
     /** whether current touch Y delta is above falsing threshold */
     private boolean mTouchAboveFalsingThreshold;
-    /** whether we are tracking a touch on QS container */
-    private boolean mTracking;
     /** pointerId of the pointer we're currently tracking */
     private int mTrackingPointer;
 
@@ -344,14 +345,16 @@
             ShadeRepository shadeRepository,
             ShadeInteractor shadeInteractor,
             JavaAdapter javaAdapter,
-            CastController castController
+            CastController castController,
+            SplitShadeStateController splitShadeStateController
     ) {
         mPanelViewControllerLazy = panelViewControllerLazy;
         mPanelView = panelView;
         mQsFrame = mPanelView.findViewById(R.id.qs_frame);
         mKeyguardStatusBar = mPanelView.findViewById(R.id.keyguard_header);
         mResources = mPanelView.getResources();
-        mSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
+        mSplitShadeStateController = splitShadeStateController;
+        mSplitShadeEnabled = mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
         mQsFrameTranslateController = qsFrameTranslateController;
         mShadeTransitionController = shadeTransitionController;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -437,7 +440,7 @@
     }
 
     void updateResources() {
-        mSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
+        mSplitShadeEnabled = mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
         if (mQs != null) {
             mQs.setInSplitShade(mSplitShadeEnabled);
         }
@@ -596,7 +599,7 @@
 
     @VisibleForTesting
     boolean isTracking() {
-        return mTracking;
+        return mShadeRepository.getLegacyQsTracking().getValue();
     }
 
     public boolean getFullyExpanded() {
@@ -609,10 +612,14 @@
         // split shade as there QS are always expanded so every collapsing motion is motion from
         // expanded QS to closed panel
         return mExpandImmediate || (mExpanded
-                && !mTracking && !isExpansionAnimating()
+                && !isTracking() && !isExpansionAnimating()
                 && !mExpansionFromOverscroll);
     }
 
+    private void setTracking(boolean tracking) {
+        mShadeRepository.setLegacyQsTracking(tracking);
+    }
+
     private boolean isQsFragmentCreated() {
         return mQs != null;
     }
@@ -1597,7 +1604,7 @@
         if (action == MotionEvent.ACTION_DOWN && expandedShadeCollapsedQs) {
             // Down in the empty area while fully expanded - go to QS.
             mShadeLog.logMotionEvent(event, "handleQsTouch: down action, QS tracking enabled");
-            mTracking = true;
+            setTracking(true);
             traceQsJank(true, false);
             mConflictingExpansionGesture = true;
             onExpansionStarted();
@@ -1612,9 +1619,9 @@
         // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS.
         if (!mSplitShadeEnabled && !mLastShadeFlingWasExpanding
                 && computeExpansionFraction() <= 0.01 && mShadeExpandedFraction < 1.0) {
-            mTracking = false;
+            setTracking(false);
         }
-        if (!isExpandImmediate() && mTracking) {
+        if (!isExpandImmediate() && isTracking()) {
             onTouch(event);
             if (!mConflictingExpansionGesture && !mSplitShadeEnabled) {
                 return true;
@@ -1658,7 +1665,7 @@
             if (shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
                 mShadeLog.logMotionEvent(event,
                         "handleQsDown: down action, QS tracking enabled");
-                mTracking = true;
+                setTracking(true);
                 onExpansionStarted();
                 mInitialHeightOnTouch = mExpansionHeight;
                 mInitialTouchY = event.getY();
@@ -1684,7 +1691,7 @@
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mShadeLog.logMotionEvent(event, "onQsTouch: down action, QS tracking enabled");
-                mTracking = true;
+                setTracking(true);
                 traceQsJank(true, false);
                 mInitialTouchY = y;
                 mInitialTouchX = x;
@@ -1721,7 +1728,7 @@
             case MotionEvent.ACTION_CANCEL:
                 mShadeLog.logMotionEvent(event,
                         "onQsTouch: up/cancel action, QS tracking disabled");
-                mTracking = false;
+                setTracking(false);
                 mTrackingPointer = -1;
                 trackMovement(event);
                 float fraction = computeExpansionFraction();
@@ -1770,13 +1777,15 @@
                     // Dragging down on the lockscreen statusbar should prohibit other interactions
                     // immediately, otherwise we'll wait on the touchslop. This is to allow
                     // dragging down to expanded quick settings directly on the lockscreen.
-                    mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+                    if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+                        mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+                    }
                 }
                 if (mExpansionAnimator != null) {
                     mInitialHeightOnTouch = mExpansionHeight;
                     mShadeLog.logMotionEvent(event,
                             "onQsIntercept: down action, QS tracking enabled");
-                    mTracking = true;
+                    setTracking(true);
                     traceQsJank(true, false);
                     mNotificationStackScrollLayoutController.cancelLongPress();
                 }
@@ -1795,7 +1804,7 @@
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
                 trackMovement(event);
-                if (mTracking) {
+                if (isTracking()) {
                     // Already tracking because onOverscrolled was called. We need to update here
                     // so we don't stop for a frame until the next touch event gets handled in
                     // onTouchEvent.
@@ -1813,9 +1822,11 @@
                         && Math.abs(h) > Math.abs(x - mInitialTouchX)
                         && shouldQuickSettingsIntercept(
                         mInitialTouchX, mInitialTouchY, h)) {
-                    mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+                    if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+                        mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+                    }
                     mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
-                    mTracking = true;
+                    setTracking(true);
                     traceQsJank(true, false);
                     onExpansionStarted();
                     mPanelViewControllerLazy.get().notifyExpandingFinished();
@@ -1835,7 +1846,7 @@
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
                 mShadeLog.logMotionEvent(event, "onQsIntercept: up action, QS tracking disabled");
-                mTracking = false;
+                setTracking(false);
                 break;
         }
         return false;
@@ -2061,7 +2072,7 @@
         ipw.print("mTouchAboveFalsingThreshold=");
         ipw.println(mTouchAboveFalsingThreshold);
         ipw.print("mTracking=");
-        ipw.println(mTracking);
+        ipw.println(isTracking());
         ipw.print("mTrackingPointer=");
         ipw.println(mTrackingPointer);
         ipw.print("mExpanded=");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 6564118..19a4ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -135,7 +135,8 @@
     private val date: TextView = header.requireViewById(R.id.date)
     private val iconContainer: StatusIconContainer = header.requireViewById(R.id.statusIcons)
     private val mShadeCarrierGroup: ShadeCarrierGroup = header.requireViewById(R.id.carrier_group)
-    private val systemIcons: View = header.requireViewById(R.id.shade_header_system_icons)
+    private val systemIconsHoverContainer: View =
+        header.requireViewById(R.id.hover_system_icons_container)
 
     private var roundedCorners = 0
     private var cutout: DisplayCutout? = null
@@ -259,14 +260,18 @@
                     header.paddingRight,
                     header.paddingBottom
                 )
-                systemIcons.setPaddingRelative(
+                systemIconsHoverContainer.setPaddingRelative(
                     resources.getDimensionPixelSize(
-                        R.dimen.shade_header_system_icons_padding_start
+                        R.dimen.hover_system_icons_container_padding_start
                     ),
-                    resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_top),
-                    resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_end),
                     resources.getDimensionPixelSize(
-                        R.dimen.shade_header_system_icons_padding_bottom
+                        R.dimen.hover_system_icons_container_padding_top
+                    ),
+                    resources.getDimensionPixelSize(
+                        R.dimen.hover_system_icons_container_padding_end
+                    ),
+                    resources.getDimensionPixelSize(
+                        R.dimen.hover_system_icons_container_padding_bottom
                     )
                 )
             }
@@ -330,8 +335,8 @@
         demoModeController.addCallback(demoModeReceiver)
         statusBarIconController.addIconGroup(iconManager)
         nextAlarmController.addCallback(nextAlarmCallback)
-        systemIcons.setOnHoverListener(
-            statusOverlayHoverListenerFactory.createListener(systemIcons)
+        systemIconsHoverContainer.setOnHoverListener(
+            statusOverlayHoverListenerFactory.createListener(systemIconsHoverContainer)
         )
     }
 
@@ -343,7 +348,7 @@
         demoModeController.removeCallback(demoModeReceiver)
         statusBarIconController.removeIconGroup(iconManager)
         nextAlarmController.removeCallback(nextAlarmCallback)
-        systemIcons.setOnHoverListener(null)
+        systemIconsHoverContainer.setOnHoverListener(null)
     }
 
     fun disable(state1: Int, state2: Int, animate: Boolean) {
@@ -479,11 +484,13 @@
         if (largeScreenActive) {
             logInstantEvent("Large screen constraints set")
             header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
-            systemIcons.setOnClickListener { shadeCollapseAction?.run() }
+            systemIconsHoverContainer.isClickable = true
+            systemIconsHoverContainer.setOnClickListener { shadeCollapseAction?.run() }
         } else {
             logInstantEvent("Small screen constraints set")
             header.setTransition(HEADER_TRANSITION_ID)
-            systemIcons.setOnClickListener(null)
+            systemIconsHoverContainer.setOnClickListener(null)
+            systemIconsHoverContainer.isClickable = false
         }
         header.jumpToState(header.startState)
         updatePosition()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index b553f0f..6ee6cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -43,24 +43,6 @@
     /** Cancels the views current animation. */
     fun cancelAnimation()
 
-    /** Input focus transfer is about to happen. */
-    fun startWaitingForExpandGesture()
-
-    /**
-     * Called when this view is no longer waiting for input focus transfer.
-     *
-     * There are two scenarios behind this function call. First, input focus transfer has
-     * successfully happened and this view already received synthetic DOWN event.
-     * (mExpectingSynthesizedDown == false). Do nothing.
-     *
-     * Second, before input focus transfer finished, user may have lifted finger in previous window
-     * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
-     * this case, we use the velocity to trigger fling event.
-     *
-     * @param velocity unit is in px / millis
-     */
-    fun stopWaitingForExpandGesture(cancel: Boolean, velocity: Float)
-
     /** Animates the view from its current alpha to zero then runs the runnable. */
     fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable): ViewPropertyAnimator
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index cdbea81..fdc049c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -199,12 +199,6 @@
     /** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
     fun transitionToExpandedShade(delay: Long)
 
-    /**
-     * Returns whether the unlock hint animation is running. The unlock hint animation is when the
-     * user taps the lock screen, causing the contents of the lock screen visually bounce.
-     */
-    val isUnlockHintRunning: Boolean
-
     /** @see ViewGroupFadeHelper.reset */
     fun resetViewGroupFade()
 
@@ -245,10 +239,35 @@
     )
     fun isFullyExpanded(): Boolean
 
-    /** Sends an external (e.g. Status Bar) touch event to the Shade touch handler. */
+    /**
+     * Sends an external (e.g. Status Bar) touch event to the Shade touch handler.
+     *
+     * This is different from [startInputFocusTransfer] as it doesn't rely on setting the launcher
+     * window slippery to allow the frameworks to route those events after passing the initial
+     * threshold.
+     */
     fun handleExternalTouch(event: MotionEvent): Boolean
 
     /**
+     * Triggered when an input focus transfer gesture has started.
+     *
+     * Used to dispatch initial touch events before crossing the threshold to pull down the
+     * notification shade. After that, since the launcher window is set to slippery, input
+     * frameworks take care of routing the events to the notification shade.
+     */
+    fun startInputFocusTransfer()
+
+    /** Triggered when the input focus transfer was cancelled. */
+    fun cancelInputFocusTransfer()
+
+    /**
+     * Triggered when the input focus transfer has finished successfully.
+     *
+     * @param velocity unit is in px / millis
+     */
+    fun finishInputFocusTransfer(velocity: Float)
+
+    /**
      * Performs haptic feedback from a view with a haptic feedback constant.
      *
      * The implementation of this method should use the [android.view.View.performHapticFeedback]
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 1893756..2ed62dd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -73,7 +73,6 @@
         return false
     }
     override fun transitionToExpandedShade(delay: Long) {}
-    override val isUnlockHintRunning: Boolean = false
 
     override fun resetViewGroupFade() {}
     override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {}
@@ -86,6 +85,9 @@
     override fun handleExternalTouch(event: MotionEvent): Boolean {
         return false
     }
+    override fun startInputFocusTransfer() {}
+    override fun cancelInputFocusTransfer() {}
+    override fun finishInputFocusTransfer(velocity: Float) {}
     override fun performHapticFeedback(constant: Int) {}
 
     override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 947259a..024c8e3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -56,6 +56,45 @@
     @Deprecated("Use ShadeInteractor.shadeExpansion instead")
     val legacyShadeExpansion: StateFlow<Float>
 
+    /**
+     * NotificationPanelViewController.mTracking as a flow. "Tracking" means that the user is moving
+     * the shade up or down with a pointer. Going forward, this concept will be replaced by checks
+     * for whether a transition was driven by user input instead of whether a pointer is currently
+     * touching the screen, i.e. after the user has lifted their finger to fling the shade, these
+     * values would be different.
+     */
+    @Deprecated("Use ShadeInteractor instead") val legacyShadeTracking: StateFlow<Boolean>
+
+    /**
+     * QuickSettingsController.mTracking as a flow. "Tracking" means that the user is moving quick
+     * settings up or down with a pointer. Going forward, this concept will be replaced by checks
+     * for whether a transition was driven by user input instead of whether a pointer is currently
+     * touching the screen, i.e. after the user has lifted their finger to fling the QS, these
+     * values would be different.
+     */
+    @Deprecated("Use ShadeInteractor instead") val legacyQsTracking: StateFlow<Boolean>
+
+    /**
+     * NotificationPanelViewController.mPanelExpanded as a flow. This value is true whenever the
+     * expansion fraction is greater than zero or NPVC is about to accept an input transfer from the
+     * status bar, home screen, or trackpad.
+     */
+    @Deprecated("Use ShadeInteractor instead")
+    val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean>
+
+    /**
+     * Sets whether the expansion fraction is greater than zero or NPVC is about to accept an input
+     * transfer from the status bar, home screen, or trackpad.
+     */
+    @Deprecated("Use ShadeInteractor instead")
+    fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
+
+    /** Sets whether the user is moving Quick Settings with a pointer */
+    fun setLegacyQsTracking(legacyQsTracking: Boolean)
+
+    /** Sets whether the user is moving the shade with a pointer */
+    fun setLegacyShadeTracking(tracking: Boolean)
+
     /** Amount shade has expanded with regard to the UDFPS location */
     val udfpsTransitionToFullShadeProgress: StateFlow<Float>
 
@@ -123,6 +162,36 @@
     @Deprecated("Use ShadeInteractor.shadeExpansion instead")
     override val legacyShadeExpansion: StateFlow<Float> = _legacyShadeExpansion.asStateFlow()
 
+    private val _legacyShadeTracking = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead")
+    override val legacyShadeTracking: StateFlow<Boolean> = _legacyShadeTracking.asStateFlow()
+
+    private val _legacyQsTracking = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead")
+    override val legacyQsTracking: StateFlow<Boolean> = _legacyQsTracking.asStateFlow()
+
+    private val _legacyExpandedOrAwaitingInputTransfer = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead")
+    override val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> =
+        _legacyExpandedOrAwaitingInputTransfer.asStateFlow()
+
+    @Deprecated("Use ShadeInteractor instead")
+    override fun setLegacyExpandedOrAwaitingInputTransfer(
+        legacyExpandedOrAwaitingInputTransfer: Boolean
+    ) {
+        _legacyExpandedOrAwaitingInputTransfer.value = legacyExpandedOrAwaitingInputTransfer
+    }
+
+    @Deprecated("Should only be called by NPVC and tests")
+    override fun setLegacyQsTracking(legacyQsTracking: Boolean) {
+        _legacyQsTracking.value = legacyQsTracking
+    }
+
+    @Deprecated("Should only be called by NPVC and tests")
+    override fun setLegacyShadeTracking(tracking: Boolean) {
+        _legacyShadeTracking.value = tracking
+    }
+
     override fun setQsExpansion(qsExpansion: Float) {
         _qsExpansion.value = qsExpansion
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 95a072c..251cc16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -35,15 +35,19 @@
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.isActive
 
 /** Business logic for shade interactions. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -54,6 +58,7 @@
     @Application scope: CoroutineScope,
     disableFlagsRepository: DisableFlagsRepository,
     sceneContainerFlags: SceneContainerFlags,
+    // TODO(b/300258424) convert to direct reference instead of provider
     sceneInteractorProvider: Provider<SceneInteractor>,
     keyguardRepository: KeyguardRepository,
     userSetupRepository: UserSetupRepository,
@@ -72,7 +77,7 @@
      * Whether split shade, the combined notifications and quick settings shade used for large
      * screens, is enabled.
      */
-    val splitShadeEnabled: Flow<Boolean> =
+    val isSplitShadeEnabled: Flow<Boolean> =
         sharedNotificationContainerInteractor.configurationBasedDimensions
             .map { dimens -> dimens.useSplitShade }
             .distinctUntilChanged()
@@ -87,7 +92,7 @@
                     keyguardRepository.statusBarState,
                     repository.legacyShadeExpansion,
                     repository.qsExpansion,
-                    splitShadeEnabled
+                    isSplitShadeEnabled
                 ) {
                     lockscreenShadeExpansion,
                     statusBarState,
@@ -119,18 +124,66 @@
             repository.qsExpansion
         }
 
-    /** The amount [0-1] either QS or the shade has been opened */
+    /** The amount [0-1] either QS or the shade has been opened. */
     val anyExpansion: StateFlow<Float> =
         combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
             .stateIn(scope, SharingStarted.Eagerly, 0f)
 
     /** Whether either the shade or QS is expanding from a fully collapsed state. */
-    val anyExpanding =
+    val isAnyExpanding: Flow<Boolean> =
         anyExpansion
             .pairwise(1f)
             .map { (prev, curr) -> curr > 0f && curr < 1f && prev < 1f }
             .distinctUntilChanged()
 
+    /**
+     * Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At
+     * this time, this is not simply a matter of checking if either value in shadeExpansion and
+     * qsExpansion is greater than zero, because it includes the legacy concept of whether input
+     * transfer is about to occur. If the scene container flag is enabled, it just checks whether
+     * either expansion value is positive.
+     *
+     * TODO(b/300258424) remove all but the first sentence of this comment
+     */
+    val isAnyExpanded: Flow<Boolean> =
+        if (sceneContainerFlags.isEnabled()) {
+            anyExpansion.map { it > 0f }.distinctUntilChanged()
+        } else {
+            repository.legacyExpandedOrAwaitingInputTransfer
+        }
+
+    /**
+     * Whether the user is expanding or collapsing the shade with user input. This will be true even
+     * if the user's input gesture has ended but a transition they initiated is animating.
+     */
+    val isUserInteractingWithShade: Flow<Boolean> =
+        if (sceneContainerFlags.isEnabled()) {
+            sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade)
+        } else {
+            userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion)
+        }
+
+    /**
+     * Whether the user is expanding or collapsing quick settings with user input. This will be true
+     * even if the user's input gesture has ended but a transition they initiated is still
+     * animating.
+     */
+    val isUserInteractingWithQs: Flow<Boolean> =
+        if (sceneContainerFlags.isEnabled()) {
+            sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+        } else {
+            userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
+        }
+
+    /**
+     * Whether the user is expanding or collapsing either the shade or quick settings with user
+     * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended
+     * but a transition they initiated is still animating.
+     */
+    val isUserInteracting: Flow<Boolean> =
+        combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
+            .distinctUntilChanged()
+
     /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
     val isExpandToQsEnabled: Flow<Boolean> =
         combine(
@@ -169,4 +222,42 @@
                 }
             }
             .distinctUntilChanged()
+
+    fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+        sceneInteractor.transitionState
+            .map { state ->
+                when (state) {
+                    is ObservableTransitionState.Idle -> false
+                    is ObservableTransitionState.Transition ->
+                        state.isUserInputDriven &&
+                            (state.toScene == sceneKey || state.fromScene == sceneKey)
+                }
+            }
+            .distinctUntilChanged()
+
+    /**
+     * Return a flow for whether a user is interacting with an expandable shade component using
+     * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that
+     * [expansion.first] checks the current value of the flow.
+     */
+    private fun userInteractingFlow(
+        tracking: Flow<Boolean>,
+        expansion: StateFlow<Float>
+    ): Flow<Boolean> {
+        return flow {
+            // initial value is false
+            emit(false)
+            while (currentCoroutineContext().isActive) {
+                // wait for tracking to become true
+                tracking.first { it }
+                emit(true)
+                // wait for tracking to become false
+                tracking.first { !it }
+                // wait for expansion to complete in either direction
+                expansion.first { it <= 0f || it >= 1f }
+                // interaction complete
+                emit(false)
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
index fd57f21..4ba5674 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
@@ -20,7 +20,7 @@
 import android.content.res.Configuration
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import javax.inject.Inject
 
 /** Interpolator responsible for the shade when on large screens. */
@@ -32,6 +32,7 @@
     private val context: Context,
     private val splitShadeInterpolator: SplitShadeInterpolator,
     private val portraitShadeInterpolator: LargeScreenPortraitShadeInterpolator,
+    private val splitShadeStateController: SplitShadeStateController
 ) : LargeScreenShadeInterpolator {
 
     private var inSplitShade = false
@@ -48,7 +49,7 @@
     }
 
     private fun updateResources() {
-        inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+        inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
     }
 
     private val impl: LargeScreenShadeInterpolator
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
index ec16109..9715070 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.content.res.Configuration
-import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QS
@@ -31,6 +30,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -45,6 +45,7 @@
     private val context: Context,
     private val scrimShadeTransitionController: ScrimShadeTransitionController,
     private val statusBarStateController: SysuiStatusBarStateController,
+    private val splitShadeStateController: SplitShadeStateController
 ) {
 
     lateinit var shadeViewController: ShadeViewController
@@ -73,7 +74,7 @@
     }
 
     private fun updateResources() {
-        inSplitShade = context.resources.getBoolean(R.bool.config_use_split_notification_shade)
+        inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
     }
 
     private fun onPanelStateChanged(@PanelState state: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt
index 5b24af0..b6a633f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt
@@ -6,14 +6,15 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import java.io.PrintWriter
 
 /** An abstract implementation of a class that controls the lockscreen to shade transition. */
 abstract class AbstractLockscreenShadeTransitionController(
     protected val context: Context,
     configurationController: ConfigurationController,
-    dumpManager: DumpManager
+    dumpManager: DumpManager,
+    private val splitShadeStateController: SplitShadeStateController
 ) : Dumpable {
 
     protected var useSplitShade = false
@@ -44,7 +45,8 @@
     }
 
     private fun updateResourcesInternal() {
-        useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+        useSplitShade = splitShadeStateController
+                .shouldUseSplitNotificationShade(context.resources)
         updateResources()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6304c1e..670fb12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -375,15 +375,8 @@
         /**
          * @see IStatusBar#showTransient(int, int, boolean).
          */
-        default void showTransient(int displayId, @InsetsType int types) { }
-
-        /**
-         * @see IStatusBar#showTransient(int, int, boolean).
-         */
         default void showTransient(int displayId, @InsetsType int types,
-                boolean isGestureOnSystemBar) {
-            showTransient(displayId, types);
-        }
+                boolean isGestureOnSystemBar) {}
 
         /**
          * @see IStatusBar#abortTransient(int, int).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index fec6112..238317c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -8,6 +8,7 @@
 import com.android.systemui.media.controls.ui.MediaHierarchyManager
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -16,12 +17,14 @@
 class LockscreenShadeKeyguardTransitionController
 @AssistedInject
 constructor(
-    private val mediaHierarchyManager: MediaHierarchyManager,
-    @Assisted private val notificationPanelController: ShadeViewController,
-    context: Context,
-    configurationController: ConfigurationController,
-    dumpManager: DumpManager
-) : AbstractLockscreenShadeTransitionController(context, configurationController, dumpManager) {
+        private val mediaHierarchyManager: MediaHierarchyManager,
+        @Assisted private val notificationPanelController: ShadeViewController,
+        context: Context,
+        configurationController: ConfigurationController,
+        dumpManager: DumpManager,
+        splitShadeStateController: SplitShadeStateController
+) : AbstractLockscreenShadeTransitionController(context, configurationController, dumpManager,
+        splitShadeStateController) {
 
     /**
      * Distance that the full shade transition takes in order for the keyguard content on
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
index df8c6ab..5f3d757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -38,7 +39,14 @@
     configurationController: ConfigurationController,
     dumpManager: DumpManager,
     @Assisted private val qsProvider: () -> QS,
-) : AbstractLockscreenShadeTransitionController(context, configurationController, dumpManager) {
+    splitShadeStateController: SplitShadeStateController
+) :
+    AbstractLockscreenShadeTransitionController(
+        context,
+        configurationController,
+        dumpManager,
+        splitShadeStateController
+    ) {
 
     private val qs: QS
         get() = qsProvider()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
index 00d3701..af4a1aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
@@ -7,6 +7,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import javax.inject.Inject
 
 /** Controls the lockscreen to shade transition for scrims. */
@@ -16,8 +17,10 @@
     private val scrimController: ScrimController,
     context: Context,
     configurationController: ConfigurationController,
-    dumpManager: DumpManager
-) : AbstractLockscreenShadeTransitionController(context, configurationController, dumpManager) {
+    dumpManager: DumpManager,
+    splitShadeStateController: SplitShadeStateController
+) : AbstractLockscreenShadeTransitionController(context, configurationController, dumpManager,
+    splitShadeStateController) {
 
     /**
      * Distance that the full shade transition takes in order for scrim to fully transition to the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 73bbbca..29ca0f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -42,7 +42,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.wm.shell.animation.Interpolators
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -79,6 +79,7 @@
     private val shadeRepository: ShadeRepository,
     private val shadeInteractor: ShadeInteractor,
     private val powerInteractor: PowerInteractor,
+    private val splitShadeStateController: SplitShadeStateController
 ) : Dumpable {
     private var pulseHeight: Float = 0f
 
@@ -267,7 +268,9 @@
             R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
         statusBarTransitionDistance = context.resources.getDimensionPixelSize(
             R.dimen.lockscreen_shade_status_bar_transition_distance)
-        useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+
+        useSplitShade = splitShadeStateController
+                .shouldUseSplitNotificationShade(context.resources)
     }
 
     fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 59c63aa..5c45f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -46,7 +46,7 @@
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.WallpaperController
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -67,6 +67,7 @@
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val dozeParameters: DozeParameters,
     private val context: Context,
+    private val splitShadeStateController: SplitShadeStateController,
     dumpManager: DumpManager,
     configurationController: ConfigurationController
 ) : ShadeExpansionListener, Dumpable {
@@ -329,7 +330,7 @@
     }
 
     private fun updateResources() {
-        inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+        inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
     }
 
     fun addListener(listener: DepthListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3f37c60..c760227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -776,7 +776,7 @@
             }
 
         } else if (viewEnd >= shelfClipStart
-                && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
+                && view.isInShelf()
                 && (mAmbientState.isShadeExpanded()
                 || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 6dd24ea..99297b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
 
@@ -31,13 +29,7 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.view.Choreographer;
-import android.view.InsetsFlags;
 import android.view.View;
-import android.view.ViewDebug;
-import android.view.WindowInsets;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.NonNull;
@@ -554,34 +546,6 @@
     }
 
     @Override
-    public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
-            @InsetsType int requestedVisibleTypes, String packageName) {
-        boolean isFullscreen = (requestedVisibleTypes & WindowInsets.Type.statusBars()) == 0
-                || (requestedVisibleTypes & WindowInsets.Type.navigationBars()) == 0;
-        if (mIsFullscreen != isFullscreen) {
-            mIsFullscreen = isFullscreen;
-            synchronized (mListeners) {
-                for (RankedListener rl : new ArrayList<>(mListeners)) {
-                    rl.mListener.onFullscreenStateChanged(isFullscreen);
-                }
-            }
-        }
-
-        // TODO (b/190543382): Finish the logging logic.
-        // This section can be removed if we don't need to print it on logcat.
-        if (DEBUG_IMMERSIVE_APPS) {
-            boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
-            String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
-            String requestedVisibleTypesString = WindowInsets.Type.toString(requestedVisibleTypes);
-            if (requestedVisibleTypesString.isEmpty()) {
-                requestedVisibleTypesString = "none";
-            }
-            Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
-                    + " requested visible types: " + requestedVisibleTypesString);
-        }
-    }
-
-    @Override
     public void setPulsing(boolean pulsing) {
         if (mPulsing != pulsing) {
             mPulsing = pulsing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 4043fce..aa32d5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -20,9 +20,6 @@
 
 import android.annotation.IntDef;
 import android.view.View;
-import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
 
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -151,12 +148,6 @@
     boolean isKeyguardRequested();
 
     /**
-     * Set the system bar attributes
-     */
-    void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
-            @InsetsType int requestedVisibleTypes, String packageName);
-
-    /**
      * Set pulsing
      */
     void setPulsing(boolean visibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 3dfe068..d058d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.dagger;
 
-import android.app.IActivityManager;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
@@ -38,7 +37,6 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.settings.DisplayTracker;
@@ -57,12 +55,10 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -73,23 +69,15 @@
 import com.android.systemui.statusbar.phone.StatusBarIconList;
 import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.time.SystemClock;
 
 import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
 /**
  * This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
  * this separate from {@link CentralSurfacesModule} module so that components that wish to build
@@ -230,51 +218,6 @@
     }
 
     /**
-     */
-    @Provides
-    @SysUISingleton
-    static OngoingCallController provideOngoingCallController(
-            Context context,
-            CommonNotifCollection notifCollection,
-            SystemClock systemClock,
-            ActivityStarter activityStarter,
-            @Main Executor mainExecutor,
-            IActivityManager iActivityManager,
-            OngoingCallLogger logger,
-            DumpManager dumpManager,
-            StatusBarWindowController statusBarWindowController,
-            SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler,
-            StatusBarStateController statusBarStateController,
-            OngoingCallFlags ongoingCallFlags) {
-
-        boolean ongoingCallInImmersiveEnabled = ongoingCallFlags.isInImmersiveEnabled();
-        Optional<StatusBarWindowController> windowController =
-                ongoingCallInImmersiveEnabled
-                        ? Optional.of(statusBarWindowController)
-                        : Optional.empty();
-        Optional<SwipeStatusBarAwayGestureHandler> gestureHandler =
-                ongoingCallInImmersiveEnabled
-                        ? Optional.of(swipeStatusBarAwayGestureHandler)
-                        : Optional.empty();
-        OngoingCallController ongoingCallController =
-                new OngoingCallController(
-                        context,
-                        notifCollection,
-                        ongoingCallFlags,
-                        systemClock,
-                        activityStarter,
-                        mainExecutor,
-                        iActivityManager,
-                        logger,
-                        dumpManager,
-                        windowController,
-                        gestureHandler,
-                        statusBarStateController);
-        ongoingCallController.init();
-        return ongoingCallController;
-    }
-
-    /**
      * {@link NotificationPanelViewController} implements two interfaces:
      *  - {@link com.android.systemui.shade.ShadeViewController}, which can be used by any class
      *    needing access to the shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
new file mode 100644
index 0000000..d1464ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+/**
+ * A module for **only** classes related to the status bar **UI element**. This module specifically
+ * should **not** include:
+ * - Classes in the `statusbar` package that are unrelated to the status bar UI.
+ * - Status bar classes that are already provided by other modules
+ *   ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule],
+ *   [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.).
+ */
+@Module
+abstract class StatusBarModule {
+    @Binds
+    abstract fun bindStatusBarModeRepository(
+        impl: StatusBarModeRepositoryImpl
+    ): StatusBarModeRepository
+
+    @Binds
+    @IntoMap
+    @ClassKey(StatusBarModeRepositoryImpl::class)
+    abstract fun bindStatusBarModeRepositoryStart(impl: StatusBarModeRepositoryImpl): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(OngoingCallController::class)
+    abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
new file mode 100644
index 0000000..9d73071
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.data.repository
+
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A repository for the current mode of the status bar on the homescreen (translucent, transparent,
+ * opaque, lights out, hidden, etc.).
+ *
+ * Note: These status bar modes are status bar *window* states that are sent to us from
+ * WindowManager, not determined internally.
+ */
+interface StatusBarModeRepository {
+    /**
+     * True if the status bar window is showing transiently and will disappear soon, and false
+     * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
+     * persistently shown.)
+     *
+     * This behavior is controlled by WindowManager via
+     * [android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE], *not* calculated
+     * internally. SysUI merely obeys the behavior sent to us.
+     */
+    val isTransientShown: StateFlow<Boolean>
+
+    /**
+     * True the focused window is fullscreen (aka immersive) and false otherwise.
+     *
+     * Typically, the only time the status bar window is hidden is when the focused window is
+     * fullscreen.
+     */
+    val isInFullscreenMode: StateFlow<Boolean>
+
+    /**
+     * Requests for the status bar to be shown transiently.
+     *
+     * TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
+     *   determined internally instead.
+     */
+    fun showTransient()
+
+    /**
+     * Requests for the status bar to be no longer showing transiently.
+     *
+     * TODO(b/277764509): Don't allow [CentralSurfaces] to set the transient mode; have it
+     *   determined internally instead.
+     */
+    fun clearTransient()
+}
+
+@SysUISingleton
+class StatusBarModeRepositoryImpl
+@Inject
+constructor(
+    @Application scope: CoroutineScope,
+    @DisplayId thisDisplayId: Int,
+    private val commandQueue: CommandQueue,
+) : StatusBarModeRepository, CoreStartable {
+
+    private val commandQueueCallback =
+        object : CommandQueue.Callbacks {
+            override fun showTransient(
+                displayId: Int,
+                @WindowInsets.Type.InsetsType types: Int,
+                isGestureOnSystemBar: Boolean,
+            ) {
+                if (isTransientRelevant(displayId, types)) {
+                    _isTransientShown.value = true
+                }
+            }
+
+            override fun abortTransient(displayId: Int, @WindowInsets.Type.InsetsType types: Int) {
+                if (isTransientRelevant(displayId, types)) {
+                    _isTransientShown.value = false
+                }
+            }
+
+            private fun isTransientRelevant(
+                displayId: Int,
+                @WindowInsets.Type.InsetsType types: Int,
+            ): Boolean {
+                return displayId == thisDisplayId && (types and WindowInsets.Type.statusBars() != 0)
+            }
+
+            override fun onSystemBarAttributesChanged(
+                displayId: Int,
+                @WindowInsetsController.Appearance appearance: Int,
+                appearanceRegions: Array<AppearanceRegion>,
+                navbarColorManagedByIme: Boolean,
+                @WindowInsetsController.Behavior behavior: Int,
+                @WindowInsets.Type.InsetsType requestedVisibleTypes: Int,
+                packageName: String,
+                letterboxDetails: Array<LetterboxDetails>,
+            ) {
+                if (displayId != thisDisplayId) return
+                _originalStatusBarAttributes.value =
+                    StatusBarAttributes(
+                        requestedVisibleTypes,
+                    )
+            }
+        }
+
+    override fun start() {
+        commandQueue.addCallback(commandQueueCallback)
+    }
+
+    private val _isTransientShown = MutableStateFlow(false)
+    override val isTransientShown: StateFlow<Boolean> = _isTransientShown.asStateFlow()
+
+    private val _originalStatusBarAttributes = MutableStateFlow<StatusBarAttributes?>(null)
+
+    override val isInFullscreenMode: StateFlow<Boolean> =
+        _originalStatusBarAttributes
+            .map { params ->
+                val requestedVisibleTypes = params?.requestedVisibleTypes ?: return@map false
+                // When the status bar is not requested visible, we assume we're in fullscreen mode.
+                requestedVisibleTypes and WindowInsets.Type.statusBars() == 0
+            }
+            .stateIn(scope, SharingStarted.Eagerly, false)
+
+    override fun showTransient() {
+        _isTransientShown.value = true
+    }
+
+    override fun clearTransient() {
+        _isTransientShown.value = false
+    }
+
+    /**
+     * Internal class keeping track of the raw status bar attributes received from the callback.
+     * Should never be exposed.
+     */
+    private data class StatusBarAttributes(
+        @WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e206141..70ccc4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -31,6 +31,7 @@
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -38,6 +39,7 @@
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.icon.IconManager;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
@@ -151,6 +153,7 @@
                                 component.getExpandableNotificationRowController();
                         rowController.init(entry);
                         entry.setRowController(rowController);
+                        maybeSetBigPictureIconManager(row, component);
                         bindRow(entry, row);
                         updateRow(entry, row);
                         inflateContentViews(entry, params, row, callback);
@@ -165,6 +168,7 @@
             return;
         }
         mLogger.logReleasingViews(entry);
+        cancelRunningJobs(entry.getRow());
         final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
         params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED);
         params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED);
@@ -172,6 +176,23 @@
         mRowContentBindStage.requestRebind(entry, null);
     }
 
+    private void maybeSetBigPictureIconManager(ExpandableNotificationRow row,
+            ExpandableNotificationRowComponent component) {
+        if (mFeatureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
+            row.setBigPictureIconManager(component.getBigPictureIconManager());
+        }
+    }
+
+    private void cancelRunningJobs(ExpandableNotificationRow row) {
+        if (row == null) {
+            return;
+        }
+        BigPictureIconManager iconManager = row.getBigPictureIconManager();
+        if (iconManager != null) {
+            iconManager.cancelJobs();
+        }
+    }
+
     /**
      * Bind row to various controllers and managers. This is only called when the row is first
      * created.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1b790fd..cb21291 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -188,12 +188,6 @@
         return super.onInterceptTouchEvent(ev);
     }
 
-    /**
-     * Called by the TouchHandler when this view is tapped. This will be called for actual taps
-     * only, i.e. taps that have been filtered by the FalsingManager.
-     */
-    public void onTap() {}
-
     /** Sets the last action up time this view was touched. */
     public void setLastActionUpTime(long eventTime) {
         mLastActionUpTime = eventTime;
@@ -227,10 +221,6 @@
         mBackgroundNormal.setState(getDrawableState());
     }
 
-    void setRippleAllowed(boolean allowed) {
-        mBackgroundNormal.setPressedAllowed(allowed);
-    }
-
     private void updateOutlineAlpha() {
         float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
         alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 028cd18..ded5ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -80,11 +80,7 @@
 
             if (ev.getAction() == MotionEvent.ACTION_UP) {
                 // If this is a false tap, capture the even so it doesn't result in a click.
-                boolean falseTap = mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
-                if (!falseTap && v instanceof ActivatableNotificationView) {
-                    ((ActivatableNotificationView) v).onTap();
-                }
-                return falseTap;
+                return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
             }
             return result;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
new file mode 100644
index 0000000..88dbb4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -0,0 +1,298 @@
+/*
+ * 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.notification.row
+
+import android.annotation.WorkerThread
+import android.app.ActivityManager
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.util.Dumpable
+import android.util.Log
+import android.util.Size
+import com.android.internal.R
+import com.android.internal.widget.NotificationDrawableConsumer
+import com.android.internal.widget.NotificationIconManager
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.PlaceHolder
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val TAG = "BigPicImageLoader"
+private const val DEBUG = false
+private const val FREE_IMAGE_DELAY_MS = 3000L
+
+/**
+ * A helper class for [com.android.internal.widget.BigPictureNotificationImageView] to lazy-load
+ * images from SysUI. It replaces the placeholder image with the fully loaded one, and vica versa.
+ *
+ * TODO(b/283082473) move the logs to a [com.android.systemui.log.LogBuffer]
+ */
+@SuppressWarnings("DumpableNotRegistered")
+class BigPictureIconManager
+@Inject
+constructor(
+    private val context: Context,
+    private val imageLoader: ImageLoader,
+    @Application private val scope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    @Background private val bgDispatcher: CoroutineDispatcher
+) : NotificationIconManager, Dumpable {
+
+    private var lastLoadingJob: Job? = null
+    private var drawableConsumer: NotificationDrawableConsumer? = null
+    private var displayedState: DrawableState = Empty(null)
+    private var viewShown = false
+
+    private var maxWidth = getMaxWidth()
+    private var maxHeight = getMaxHeight()
+
+    /**
+     * Called when the displayed state changes of the view.
+     *
+     * @param shown true if the view is shown, and the image needs to be displayed.
+     */
+    fun onViewShown(shown: Boolean) {
+        log("onViewShown:$shown")
+
+        if (this.viewShown != shown) {
+            this.viewShown = shown
+
+            val state = displayedState
+
+            this.lastLoadingJob?.cancel()
+            this.lastLoadingJob =
+                when {
+                    state is Empty && shown -> state.icon?.let(::startLoadingJob)
+                    state is PlaceHolder && shown -> startLoadingJob(state.icon)
+                    state is FullImage && !shown ->
+                        startFreeImageJob(state.icon, state.drawableSize)
+                    else -> null
+                }
+        }
+    }
+
+    /**
+     * Update the maximum width and height allowed for bitmaps, ex. after a configuration change.
+     */
+    fun updateMaxImageSizes() {
+        log("updateMaxImageSizes")
+        maxWidth = getMaxWidth()
+        maxHeight = getMaxHeight()
+    }
+
+    /** Cancels all currently running jobs. */
+    fun cancelJobs() {
+        lastLoadingJob?.cancel()
+    }
+
+    @WorkerThread
+    override fun updateIcon(drawableConsumer: NotificationDrawableConsumer, icon: Icon?): Runnable {
+        if (this.drawableConsumer != null && this.drawableConsumer != drawableConsumer) {
+            Log.wtf(TAG, "A consumer is already set for this iconManager.")
+            return Runnable {}
+        }
+
+        if (displayedState.iconSameAs(icon)) {
+            // We're already handling this icon, nothing to do here.
+            log("skipping updateIcon for consumer:$drawableConsumer with icon:$icon")
+            return Runnable {}
+        }
+
+        this.drawableConsumer = drawableConsumer
+        this.displayedState = Empty(icon)
+        this.lastLoadingJob?.cancel()
+
+        val drawable = loadImageOrPlaceHolderSync(icon)
+
+        log("icon updated")
+
+        return Runnable { drawableConsumer.setImageDrawable(drawable) }
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>?) {
+        pw.println("BigPictureIconManager ${getDebugString()}")
+    }
+
+    @WorkerThread
+    private fun loadImageOrPlaceHolderSync(icon: Icon?): Drawable? {
+        icon ?: return null
+
+        if (viewShown) {
+            return loadImageSync(icon)
+        }
+
+        return loadPlaceHolderSync(icon) ?: loadImageSync(icon)
+    }
+
+    @WorkerThread
+    private fun loadImageSync(icon: Icon): Drawable? {
+        return imageLoader.loadDrawableSync(icon, context, maxWidth, maxHeight)?.also { drawable ->
+            checkPlaceHolderSizeForDrawable(this.displayedState, drawable)
+            this.displayedState = FullImage(icon, drawable.intrinsicSize)
+        }
+    }
+
+    private fun checkPlaceHolderSizeForDrawable(
+        displayedState: DrawableState,
+        newDrawable: Drawable
+    ) {
+        if (displayedState is PlaceHolder) {
+            val (oldWidth, oldHeight) = displayedState.drawableSize
+            val (newWidth, newHeight) = newDrawable.intrinsicSize
+
+            if (oldWidth != newWidth || oldHeight != newHeight) {
+                Log.e(
+                    TAG,
+                    "Mismatch in dimensions, when replacing PlaceHolder " +
+                        "$oldWidth X $oldHeight with Drawable $newWidth X $newHeight."
+                )
+            }
+        }
+    }
+
+    @WorkerThread
+    private fun loadPlaceHolderSync(icon: Icon): Drawable? {
+        return imageLoader
+            .loadSizeSync(icon, context)
+            ?.resizeToMax(maxWidth, maxHeight) // match the dimensions of the fully loaded drawable
+            ?.let { size -> createPlaceHolder(size) }
+            ?.also { drawable -> this.displayedState = PlaceHolder(icon, drawable.intrinsicSize) }
+    }
+
+    private fun startLoadingJob(icon: Icon): Job =
+        scope.launch {
+            val drawable = withContext(bgDispatcher) { loadImageSync(icon) }
+            withContext(mainDispatcher) { drawableConsumer?.setImageDrawable(drawable) }
+            log("image loaded")
+        }
+
+    private fun startFreeImageJob(icon: Icon, drawableSize: Size): Job =
+        scope.launch {
+            delay(FREE_IMAGE_DELAY_MS)
+            val drawable = createPlaceHolder(drawableSize)
+            displayedState = PlaceHolder(icon, drawable.intrinsicSize)
+            withContext(mainDispatcher) { drawableConsumer?.setImageDrawable(drawable) }
+            log("placeholder loaded")
+        }
+
+    private fun createPlaceHolder(size: Size): Drawable {
+        return PlaceHolderDrawable(width = size.width, height = size.height)
+    }
+
+    private fun isLowRam(): Boolean {
+        return ActivityManager.isLowRamDeviceStatic()
+    }
+
+    private fun getMaxWidth() =
+        context.resources.getDimensionPixelSize(
+            if (isLowRam()) {
+                R.dimen.notification_big_picture_max_width_low_ram
+            } else {
+                R.dimen.notification_big_picture_max_width
+            }
+        )
+
+    private fun getMaxHeight() =
+        context.resources.getDimensionPixelSize(
+            if (isLowRam()) {
+                R.dimen.notification_big_picture_max_height_low_ram
+            } else {
+                R.dimen.notification_big_picture_max_height
+            }
+        )
+
+    private fun log(msg: String) {
+        if (DEBUG) {
+            Log.d(TAG, "$msg state=${getDebugString()}")
+        }
+    }
+
+    private fun getDebugString() =
+        "{ state:$displayedState, hasConsumer:${drawableConsumer != null}, viewShown:$viewShown}"
+
+    private sealed class DrawableState(open val icon: Icon?) {
+        data class Empty(override val icon: Icon?) : DrawableState(icon)
+        data class PlaceHolder(override val icon: Icon, val drawableSize: Size) :
+            DrawableState(icon)
+        data class FullImage(override val icon: Icon, val drawableSize: Size) : DrawableState(icon)
+
+        fun iconSameAs(other: Icon?): Boolean {
+            val displayedIcon = icon
+            return when {
+                displayedIcon == null && other == null -> true
+                displayedIcon != null && other != null -> displayedIcon.sameAs(other)
+                else -> false
+            }
+        }
+    }
+}
+
+/**
+ * @return an image size that conforms to the maxWidth / maxHeight parameters. It can be the same
+ *   instance, if the provided size was already small enough.
+ */
+private fun Size.resizeToMax(maxWidth: Int, maxHeight: Int): Size {
+    if (width <= maxWidth && height <= maxHeight) {
+        return this
+    }
+
+    // Calculate the scale factor for both dimensions
+    val wScale =
+        if (maxWidth <= 0) {
+            1.0f
+        } else {
+            maxWidth.toFloat() / width.toFloat()
+        }
+
+    val hScale =
+        if (maxHeight <= 0) {
+            1.0f
+        } else {
+            maxHeight.toFloat() / height.toFloat()
+        }
+
+    // Scale down to the smaller scale factor
+    val scale = min(wScale, hScale)
+    if (scale < 1.0f) {
+        val targetWidth = (width * scale).toInt()
+        val targetHeight = (height * scale).toInt()
+
+        return Size(targetWidth, targetHeight)
+    }
+
+    return this
+}
+
+private val Drawable.intrinsicSize
+    get() = Size(/*width=*/ intrinsicWidth, /*height=*/ intrinsicHeight)
+
+private operator fun Size.component1() = width
+
+private operator fun Size.component2() = height
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt
new file mode 100644
index 0000000..e226665
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureLayoutInflaterFactory.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.BigPictureNotificationImageView
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import javax.inject.Inject
+
+class BigPictureLayoutInflaterFactory @Inject constructor() : NotifRemoteViewsFactory {
+
+    override fun instantiate(
+        row: ExpandableNotificationRow,
+        @InflationFlag layoutType: Int,
+        parent: View?,
+        name: String,
+        context: Context,
+        attrs: AttributeSet
+    ): View? {
+        // Currently the [BigPictureIconManager] only handles one view per notification.
+        // Exclude other layout types for now, to make sure that we set the same iconManager
+        // on only one [BigPictureNotificationImageView].
+        if (layoutType != FLAG_CONTENT_VIEW_EXPANDED) {
+            return null
+        }
+
+        return when (name) {
+            BigPictureNotificationImageView::class.java.name ->
+                BigPictureNotificationImageView(context, attrs).also { view ->
+                    view.setIconManager(row.bigPictureIconManager)
+                }
+            else -> null
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index c02382d..acece33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -376,6 +376,7 @@
     private float mTranslationWhenRemoved;
     private boolean mWasChildInGroupWhenRemoved;
     private NotificationInlineImageResolver mImageResolver;
+    private BigPictureIconManager mBigPictureIconManager;
     @Nullable
     private OnExpansionChangedListener mExpansionChangedListener;
     @Nullable
@@ -592,7 +593,6 @@
         mPublicLayout.updateExpandButtons(true);
         updateLimits();
         updateShelfIconColor();
-        updateRippleAllowed();
         if (mUpdateSelfBackgroundOnUpdate) {
             // Because this is triggered by UiMode change which we already propagated to children,
             // we know that child rows will receive the same event, and will update their own
@@ -1355,6 +1355,9 @@
         if (mImageResolver != null) {
             mImageResolver.updateMaxImageSizes();
         }
+        if (mBigPictureIconManager != null) {
+            mBigPictureIconManager.updateMaxImageSizes();
+        }
     }
 
     public void onUiModeChanged() {
@@ -1794,6 +1797,16 @@
         return mImageResolver;
     }
 
+    public BigPictureIconManager getBigPictureIconManager() {
+        return mBigPictureIconManager;
+    }
+
+    public void setBigPictureIconManager(
+            BigPictureIconManager bigPictureIconManager) {
+        mBigPictureIconManager = bigPictureIconManager;
+    }
+
+
     /**
      * Resets this view so it can be re-used for an updated notification.
      */
@@ -2556,31 +2569,8 @@
                 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
             }
         }
-        updateRippleAllowed();
     }
 
-    private void updateRippleAllowed() {
-        boolean allowed = isOnKeyguard()
-                || mEntry.getSbn().getNotification().contentIntent == null;
-        setRippleAllowed(allowed);
-    }
-
-    @Override
-    public void onTap() {
-        // This notification will expand and animates into the content activity, so we disable the
-        // ripple. We will restore its value once the tap/click is actually performed.
-        if (mEntry.getSbn().getNotification().contentIntent != null) {
-            setRippleAllowed(false);
-        }
-    }
-
-    @Override
-    public boolean performClick() {
-        // We force-disabled the ripple in onTap. When this method is called, the code drawing the
-        // ripple will already have been called so we can restore its value now.
-        updateRippleAllowed();
-        return super.performClick();
-    }
 
     @Override
     public int getHeightWithoutLockscreenConstraints() {
@@ -3687,6 +3677,9 @@
                 pw.println("no viewState!!!");
             }
             pw.println(getRoundableState().debugString());
+            if (mBigPictureIconManager != null) {
+                mBigPictureIconManager.dump(pw, args);
+            }
             dumpBackgroundView(pw, args);
 
             int transientViewCount = mChildrenContainer == null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 647505c..1fd4d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -32,7 +32,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 
@@ -59,7 +58,6 @@
     private int mExpandAnimationWidth = -1;
     private int mExpandAnimationHeight = -1;
     private int mDrawableAlpha = 255;
-    private boolean mIsPressedAllowed;
 
     public NotificationBackgroundView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -153,9 +151,17 @@
 
     public void setTint(int tintColor) {
         if (tintColor != 0) {
-            mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
+            ColorStateList stateList = new ColorStateList(new int[][]{
+                    new int[]{com.android.internal.R.attr.state_pressed},
+                    new int[]{com.android.internal.R.attr.state_hovered},
+                    new int[]{}},
+
+                    new int[]{0, 0, tintColor}
+            );
+            mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
+            mBackground.setTintList(stateList);
         } else {
-            mBackground.clearColorFilter();
+            mBackground.setTintList(null);
         }
         mTintColor = tintColor;
         invalidate();
@@ -210,10 +216,6 @@
 
     public void setState(int[] drawableState) {
         if (mBackground != null && mBackground.isStateful()) {
-            if (!mIsPressedAllowed) {
-                drawableState = ArrayUtils.removeInt(drawableState,
-                        com.android.internal.R.attr.state_pressed);
-            }
             mBackground.setState(drawableState);
         }
     }
@@ -267,9 +269,12 @@
             return;
         }
         if (mBackground instanceof LayerDrawable) {
-            GradientDrawable gradientDrawable =
-                    (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
-            gradientDrawable.setCornerRadii(mCornerRadii);
+            int numberOfLayers = ((LayerDrawable) mBackground).getNumberOfLayers();
+            for (int i = 0; i < numberOfLayers; i++) {
+                GradientDrawable gradientDrawable =
+                        (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(i);
+                gradientDrawable.setCornerRadii(mCornerRadii);
+            }
         }
     }
 
@@ -295,10 +300,6 @@
         invalidate();
     }
 
-    public void setPressedAllowed(boolean allowed) {
-        mIsPressedAllowed = allowed;
-    }
-
     @Override
     public void dump(PrintWriter pw, @NonNull String[] args) {
         pw.println("mDontModifyCorners: " + mDontModifyCorners);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 867e08b..0239afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -60,12 +60,16 @@
     @Named(NOTIF_REMOTEVIEWS_FACTORIES)
     static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories(
             FeatureFlags featureFlags,
-            PrecomputedTextViewFactory precomputedTextViewFactory
+            PrecomputedTextViewFactory precomputedTextViewFactory,
+            BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory
     ) {
         final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>();
         if (featureFlags.isEnabled(Flags.PRECOMPUTED_TEXT)) {
             replacementFactories.add(precomputedTextViewFactory);
         }
+        if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
+            replacementFactories.add(bigPictureLayoutInflaterFactory);
+        }
         return replacementFactories;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt
new file mode 100644
index 0000000..40aa27f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PlaceHolderDrawable.kt
@@ -0,0 +1,33 @@
+package com.android.systemui.statusbar.notification.row
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.drawable.Drawable
+
+class PlaceHolderDrawable(private val width: Int, private val height: Int) : Drawable() {
+
+    companion object {
+        fun createFrom(other: Drawable): PlaceHolderDrawable {
+            return PlaceHolderDrawable(other.intrinsicWidth, other.intrinsicHeight)
+        }
+    }
+
+    override fun getIntrinsicWidth(): Int {
+        return width
+    }
+
+    override fun getIntrinsicHeight(): Int {
+        return height
+    }
+
+    override fun draw(canvas: Canvas) {}
+    override fun setAlpha(alpha: Int) {}
+    override fun setColorFilter(colorFilter: ColorFilter?) {}
+
+    @Suppress("DeprecatedCallableAddReplaceWith")
+    @Deprecated("Deprecated in android.graphics.drawable.Drawable")
+    override fun getOpacity(): Int {
+        return PixelFormat.TRANSPARENT
+    }
+}
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 96547db..0c4ffe2 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
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.view.View
-import android.widget.TextView
 import com.android.internal.widget.ConversationLayout
 import com.android.internal.widget.ImageFloatingTextView
 import com.android.internal.widget.MessagingLayout
@@ -36,8 +35,6 @@
         attrs: AttributeSet
     ): View? {
         return when (name) {
-            TextView::class.java.name,
-            TextView::class.java.simpleName -> PrecomputedTextView(context, attrs)
             ImageFloatingTextView::class.java.name ->
                 PrecomputedImageFloatingTextView(context, attrs)
             MessagingLayout::class.java.name ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 3588621..0a6a2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -23,6 +23,7 @@
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -68,6 +69,12 @@
     ExpandableNotificationRowController getExpandableNotificationRowController();
 
     /**
+     * Creates a BigPictureIconManager.
+     */
+    @NotificationRowScope
+    BigPictureIconManager getBigPictureIconManager();
+
+    /**
      * Dagger Module that extracts interesting properties from an ExpandableNotificationRow.
      */
     @Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index 54af107..9a54de1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -82,11 +82,7 @@
         }
         if (ev.action == MotionEvent.ACTION_UP) {
             // If this is a false tap, capture the even so it doesn't result in a click.
-            val falseTap: Boolean = falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
-            if (!falseTap && v is ActivatableNotificationView) {
-                v.onTap()
-            }
-            return falseTap
+            return falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
         }
         return result
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 175ba15..acd6cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -28,6 +28,7 @@
 import com.android.internal.R;
 import com.android.internal.widget.BigPictureNotificationImageView;
 import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -66,6 +67,17 @@
         }
     }
 
+    @Override
+    public void setVisible(boolean visible) {
+        super.setVisible(visible);
+
+        BigPictureIconManager imageManager = mRow.getBigPictureIconManager();
+        if (imageManager != null) {
+            // TODO(b/283082473) call it a bit earlier for true, as soon as the row starts to expand
+            imageManager.onViewShown(visible);
+        }
+    }
+
     /**
      * Starts or stops the animations in any drawables contained in this BigPicture Notification.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 95e74f2..38a368e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -86,7 +86,6 @@
     private boolean mExpansionChanging;
     private boolean mIsSmallScreen;
     private boolean mPulsing;
-    private boolean mUnlockHintRunning;
     private float mHideAmount;
     private boolean mAppearing;
     private float mPulseHeight = MAX_PULSE_HEIGHT;
@@ -592,14 +591,6 @@
         mIsSmallScreen = smallScreen;
     }
 
-    public void setUnlockHintRunning(boolean unlockHintRunning) {
-        mUnlockHintRunning = unlockHintRunning;
-    }
-
-    public boolean isUnlockHintRunning() {
-        return mUnlockHintRunning;
-    }
-
     /**
      * @return Whether we need to do a fling down after swiping up on lockscreen.
      */
@@ -770,7 +761,6 @@
         pw.println("mPulseHeight=" + mPulseHeight);
         pw.println("mTrackedHeadsUpRow.key=" + logKey(mTrackedHeadsUpRow));
         pw.println("mMaxHeadsUpTranslation=" + mMaxHeadsUpTranslation);
-        pw.println("mUnlockHintRunning=" + mUnlockHintRunning);
         pw.println("mDozeAmount=" + mDozeAmount);
         pw.println("mDozing=" + mDozing);
         pw.println("mFractionToShade=" + mFractionToShade);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 5e3a67e..e8521d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -118,9 +118,9 @@
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.DumpUtilsKt;
-import com.android.systemui.util.LargeScreenUtils;
 
 import com.google.errorprone.annotations.CompileTimeConstant;
 
@@ -568,6 +568,13 @@
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private boolean mShouldUseSplitNotificationShade;
     private boolean mHasFilteredOutSeenNotifications;
+    @Nullable private SplitShadeStateController mSplitShadeStateController = null;
+
+    /** Pass splitShadeStateController to view and update split shade */
+    public void passSplitShadeStateController(SplitShadeStateController splitShadeStateController) {
+        mSplitShadeStateController = splitShadeStateController;
+        updateSplitNotificationShade();
+    }
 
     private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
             new ExpandableView.OnHeightChangedListener() {
@@ -630,7 +637,6 @@
         mSectionsManager = Dependency.get(NotificationSectionsManager.class);
         mScreenOffAnimationController =
                 Dependency.get(ScreenOffAnimationController.class);
-        updateSplitNotificationShade();
         mSectionsManager.initialize(this);
         mSections = mSectionsManager.createSectionsForBuckets();
 
@@ -1350,8 +1356,7 @@
      */
     private boolean shouldSkipHeightUpdate() {
         return mAmbientState.isOnKeyguard()
-                && (mAmbientState.isUnlockHintRunning()
-                || mAmbientState.isSwipingUp()
+                && (mAmbientState.isSwipingUp()
                 || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
     }
 
@@ -5071,14 +5076,6 @@
         mAmbientState.setSmallScreen(isFullWidth);
     }
 
-    public void setUnlockHintRunning(boolean running) {
-        mAmbientState.setUnlockHintRunning(running);
-        if (!running) {
-            // re-calculate the stack height which was frozen while running this animation
-            updateStackPosition();
-        }
-    }
-
     public void setPanelFlinging(boolean flinging) {
         mAmbientState.setFlinging(flinging);
         if (!flinging) {
@@ -5675,7 +5672,7 @@
 
     @VisibleForTesting
     void updateSplitNotificationShade() {
-        boolean split = LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+        boolean split = mSplitShadeStateController.shouldUseSplitNotificationShade(getResources());
         if (split != mShouldUseSplitNotificationShade) {
             mShouldUseSplitNotificationShade = split;
             updateDismissBehavior();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 93b5ff7..b051809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -128,6 +128,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.Compile;
@@ -672,7 +673,8 @@
             NotificationTargetsHelper notificationTargetsHelper,
             SecureSettings secureSettings,
             NotificationDismissibilityProvider dismissibilityProvider,
-            ActivityStarter activityStarter) {
+            ActivityStarter activityStarter,
+            SplitShadeStateController splitShadeStateController) {
         mView = view;
         mKeyguardTransitionRepo = keyguardTransitionRepo;
         mStackStateLogger = stackLogger;
@@ -722,6 +724,7 @@
         mSecureSettings = secureSettings;
         mDismissibilityProvider = dismissibilityProvider;
         mActivityStarter = activityStarter;
+        mView.passSplitShadeStateController(splitShadeStateController);
         updateResources();
         setUpView();
     }
@@ -1200,10 +1203,6 @@
         mView.setHeadsUpBoundaries(height, bottomBarHeight);
     }
 
-    public void setUnlockHintRunning(boolean running) {
-        mView.setUnlockHintRunning(running);
-    }
-
     public void setPanelFlinging(boolean flinging) {
         mView.setPanelFlinging(flinging);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index c7cb70c..24104d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -29,8 +29,8 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Compile
-import com.android.systemui.util.LargeScreenUtils.shouldUseSplitNotificationShade
 import com.android.systemui.util.children
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -54,7 +54,8 @@
     private val statusBarStateController: SysuiStatusBarStateController,
     private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
     private val mediaDataManager: MediaDataManager,
-    @Main private val resources: Resources
+    @Main private val resources: Resources,
+    private val splitShadeStateController: SplitShadeStateController
 ) {
 
     /**
@@ -181,7 +182,8 @@
 
         // How many notifications we can show at heightWithoutLockscreenConstraints
         var minCountAtHeightWithoutConstraints =
-            if (isMediaShowing && !shouldUseSplitNotificationShade(resources)) 2 else 1
+            if (isMediaShowing && !splitShadeStateController
+                    .shouldUseSplitNotificationShade(resources)) 2 else 1
         log {
             "\t---maxNotifWithoutSavingSpace=$maxNotifWithoutSavingSpace " +
                 "isMediaShowing=$isMediaShowing" +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 4ed31c2..51b6c75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.R
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,6 +37,7 @@
 constructor(
     configurationRepository: ConfigurationRepository,
     private val context: Context,
+    private val splitShadeStateController: SplitShadeStateController
 ) {
 
     private val _topPosition = MutableStateFlow(0f)
@@ -47,7 +49,10 @@
             .map { _ ->
                 with(context.resources) {
                     ConfigurationBasedDimensions(
-                        useSplitShade = getBoolean(R.bool.config_use_split_notification_shade),
+                        useSplitShade =
+                            splitShadeStateController.shouldUseSplitNotificationShade(
+                                context.resources
+                            ),
                         useLargeScreenHeader =
                             getBoolean(R.bool.config_use_large_screen_shade_header),
                         marginHorizontal =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 688843d..e52d604 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.view.View
+import androidx.constraintlayout.core.widgets.Optimizer
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -45,6 +46,7 @@
     private val baseConstraintSet = ConstraintSet()
 
     init {
+        optimizationLevel = optimizationLevel or Optimizer.OPTIMIZATION_GRAPH
         baseConstraintSet.apply {
             create(R.id.nssl_guideline, VERTICAL)
             setGuidelinePercent(R.id.nssl_guideline, 0.5f)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index cfa481e..3e1f09f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -25,7 +25,6 @@
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.view.MotionEvent;
 import android.view.RemoteAnimationAdapter;
 import android.view.View;
 import android.window.RemoteTransition;
@@ -197,21 +196,6 @@
 
     void onKeyguardViewManagerStatesUpdated();
 
-    /**
-     * Used to dispatch initial touch events before crossing the threshold to pull down the
-     * notification shade. After that, since the launcher window is set to slippery, input
-     * frameworks take care of routing the events to the notification shade.
-     */
-    void onInputFocusTransfer(boolean start, boolean cancel, float velocity);
-
-    /**
-     * Dispatches status bar motion event to the notification shade. This is different from
-     * {@link #onInputFocusTransfer(boolean, boolean, float)} as it doesn't rely on setting the
-     * launcher window slippery to allow the frameworks to route those events after passing the
-     * initial threshold.
-     */
-    default void onStatusBarTrackpadEvent(MotionEvent event) {}
-
     /** */
     boolean getCommandQueuePanelsEnabled();
 
@@ -263,14 +247,10 @@
     // TODO: Figure out way to remove these.
     NavigationBarView getNavigationBarView();
 
-    boolean isOverviewEnabled();
-
     void setBouncerShowing(boolean bouncerShowing);
 
     boolean isScreenFullyOff();
 
-    void showScreenPinningRequest(int taskId, boolean allowCancel);
-
     @Nullable
     Intent getEmergencyActionIntent();
 
@@ -301,8 +281,6 @@
 
     boolean isBouncerShowingScrimmed();
 
-    boolean isBouncerShowingOverDream();
-
     void updateNotificationPanelTouchState();
 
     int getRotation();
@@ -310,10 +288,6 @@
     @VisibleForTesting
     void setBarStateForTest(int state);
 
-    void showTransientUnchecked();
-
-    void clearTransient();
-
     void acquireGestureWakeLock(long time);
 
     boolean setAppearance(int appearance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 28bb581..c9db153 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -36,7 +36,6 @@
 import android.util.Slog;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
-import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -59,6 +58,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
 import com.android.systemui.shade.QuickSettingsController;
@@ -84,6 +84,7 @@
 public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
     private final CentralSurfaces mCentralSurfaces;
     private final Context mContext;
+    private final ScreenPinningRequest mScreenPinningRequest;
     private final com.android.systemui.shade.ShadeController mShadeController;
     private final CommandQueue mCommandQueue;
     private final ShadeViewController mShadeViewController;
@@ -126,6 +127,7 @@
             QuickSettingsController quickSettingsController,
             Context context,
             @Main Resources resources,
+            ScreenPinningRequest screenPinningRequest,
             ShadeController shadeController,
             CommandQueue commandQueue,
             ShadeViewController shadeViewController,
@@ -155,6 +157,7 @@
         mCentralSurfaces = centralSurfaces;
         mQsController = quickSettingsController;
         mContext = context;
+        mScreenPinningRequest = screenPinningRequest;
         mShadeController = shadeController;
         mCommandQueue = commandQueue;
         mShadeViewController = shadeViewController;
@@ -188,17 +191,6 @@
     }
 
     @Override
-    public void abortTransient(int displayId, @InsetsType int types) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        if ((types & WindowInsets.Type.statusBars()) == 0) {
-            return;
-        }
-        mCentralSurfaces.clearTransient();
-    }
-
-    @Override
     public void addQsTile(ComponentName tile) {
         mQSHost.addTile(tile);
     }
@@ -483,17 +475,6 @@
     }
 
     @Override
-    public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        if ((types & WindowInsets.Type.statusBars()) == 0) {
-            return;
-        }
-        mCentralSurfaces.showTransientUnchecked();
-    }
-
-    @Override
     public void toggleKeyboardShortcutsMenu(int deviceId) {
         mCentralSurfaces.resendMessage(new CentralSurfaces.KeyboardShortcutsMessage(deviceId));
     }
@@ -516,7 +497,7 @@
             return;
         }
         // Show screen pinning request, since this comes from an app, show 'no thanks', button.
-        mCentralSurfaces.showScreenPinningRequest(taskId, true);
+        mScreenPinningRequest.showPrompt(taskId, true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index ff380db..3cb5e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -39,7 +39,6 @@
     override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
     override fun isLaunchingActivityOverLockscreen() = false
     override fun onKeyguardViewManagerStatesUpdated() {}
-    override fun onInputFocusTransfer(start: Boolean, cancel: Boolean, velocity: Float) {}
     override fun getCommandQueuePanelsEnabled() = false
     override fun showWirelessChargingAnimation(batteryLevel: Int) {}
     override fun checkBarModes() {}
@@ -68,10 +67,8 @@
         cancelAction: Runnable?,
     ) {}
     override fun getNavigationBarView(): NavigationBarView? = null
-    override fun isOverviewEnabled() = false
     override fun setBouncerShowing(bouncerShowing: Boolean) {}
     override fun isScreenFullyOff() = false
-    override fun showScreenPinningRequest(taskId: Int, allowCancel: Boolean) {}
     override fun getEmergencyActionIntent(): Intent? = null
     override fun isCameraAllowedByAdmin() = false
     override fun isGoingToSleep() = false
@@ -84,12 +81,9 @@
     override fun awakenDreams() {}
     override fun isBouncerShowing() = false
     override fun isBouncerShowingScrimmed() = false
-    override fun isBouncerShowingOverDream() = false
     override fun updateNotificationPanelTouchState() {}
     override fun getRotation() = 0
     override fun setBarStateForTest(state: Int) {}
-    override fun showTransientUnchecked() {}
-    override fun clearTransient() {}
     override fun acquireGestureWakeLock(time: Long) {}
     override fun setAppearance(appearance: Int) = false
     override fun getBarMode() = 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 490c469..6b260ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -87,7 +87,6 @@
 import android.view.Display;
 import android.view.IRemoteAnimationRunner;
 import android.view.IWindowManager;
-import android.view.MotionEvent;
 import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.WindowInsets;
@@ -168,7 +167,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.settings.UserTracker;
@@ -208,6 +206,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -239,6 +238,7 @@
 import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.MessageRouter;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
@@ -388,7 +388,6 @@
      */
     protected int mState; // TODO: remove this. Just use StatusBarStateController
     protected boolean mBouncerShowing;
-    private boolean mBouncerShowingOverDream;
 
     private final PhoneStatusBarPolicy mIconPolicy;
 
@@ -412,6 +411,7 @@
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarInitializer mStatusBarInitializer;
     private final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarModeRepository mStatusBarModeRepository;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @VisibleForTesting
     DozeServiceHost mDozeServiceHost;
@@ -498,8 +498,6 @@
     /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
     private @Appearance int mAppearance;
 
-    private boolean mTransientShown;
-
     private final DisplayMetrics mDisplayMetrics;
 
     // XXX: gesture research
@@ -507,8 +505,6 @@
         ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
         : null;
 
-    private final ScreenPinningRequest mScreenPinningRequest;
-
     private final MetricsLogger mMetricsLogger;
 
     // ensure quick settings is disabled until the current user makes it through the setup wizard
@@ -562,11 +558,10 @@
     private final ScrimController mScrimController;
     protected DozeScrimController mDozeScrimController;
     private final BackActionInteractor mBackActionInteractor;
+    private final JavaAdapter mJavaAdapter;
     private final Executor mUiBgExecutor;
 
     protected boolean mDozing;
-    private boolean mIsFullscreen;
-
     boolean mCloseQsBeforeScreenOff;
 
     private final NotificationMediaManager mMediaManager;
@@ -637,6 +632,7 @@
             StatusBarInitializer statusBarInitializer,
             StatusBarWindowController statusBarWindowController,
             StatusBarWindowStateController statusBarWindowStateController,
+            StatusBarModeRepository statusBarModeRepository,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarSignalPolicy statusBarSignalPolicy,
             PulseExpansionHandler pulseExpansionHandler,
@@ -655,6 +651,7 @@
             DisplayMetrics displayMetrics,
             MetricsLogger metricsLogger,
             ShadeLogger shadeLogger,
+            JavaAdapter javaAdapter,
             @UiBackground Executor uiBgExecutor,
             ShadeSurface shadeSurface,
             NotificationMediaManager notificationMediaManager,
@@ -692,7 +689,6 @@
             DozeServiceHost dozeServiceHost,
             BackActionInteractor backActionInteractor,
             PowerManager powerManager,
-            ScreenPinningRequest screenPinningRequest,
             DozeScrimController dozeScrimController,
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
@@ -745,6 +741,7 @@
         mAutoHideController = autoHideController;
         mStatusBarInitializer = statusBarInitializer;
         mStatusBarWindowController = statusBarWindowController;
+        mStatusBarModeRepository = statusBarModeRepository;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPulseExpansionHandler = pulseExpansionHandler;
         mWakeUpCoordinator = notificationWakeUpCoordinator;
@@ -764,6 +761,7 @@
         mDisplayMetrics = displayMetrics;
         mMetricsLogger = metricsLogger;
         mShadeLogger = shadeLogger;
+        mJavaAdapter = javaAdapter;
         mUiBgExecutor = uiBgExecutor;
         mShadeSurface = shadeSurface;
         mMediaManager = notificationMediaManager;
@@ -799,7 +797,6 @@
         mDozeParameters = dozeParameters;
         mScrimController = scrimController;
         mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
-        mScreenPinningRequest = screenPinningRequest;
         mDozeScrimController = dozeScrimController;
         mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
         mAuthRippleController = authRippleController;
@@ -940,7 +937,7 @@
         setUpPresenter();
 
         if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
-            showTransientUnchecked();
+            mStatusBarModeRepository.showTransient();
         }
         mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
                 result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
@@ -1190,6 +1187,11 @@
         mWallpaperController.setRootView(getNotificationShadeWindowView());
 
         mDemoModeController.addCallback(mDemoModeCallback);
+        mJavaAdapter.alwaysCollectFlow(
+                mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged);
+        mJavaAdapter.alwaysCollectFlow(
+                mStatusBarModeRepository.isInFullscreenMode(),
+                this::onStatusBarFullscreenChanged);
 
         mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
         mCommandQueue.addCallback(mCommandQueueCallbacks);
@@ -1255,7 +1257,7 @@
 
             @Override
             public void hide() {
-                clearTransient();
+                mStatusBarModeRepository.clearTransient();
             }
         });
 
@@ -1543,7 +1545,9 @@
         return (v, event) -> {
             mAutoHideController.checkUserAutoHide(event);
             mRemoteInputManager.checkRemoteInputOutside(event);
-            mShadeController.onStatusBarTouch(event);
+            if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+                mShadeController.onStatusBarTouch(event);
+            }
             return getNotificationShadeWindowView().onTouchEvent(event);
         };
     }
@@ -1687,27 +1691,6 @@
         mHeadsUpManager.releaseAllImmediately();
     }
 
-    /**
-     * Called when another window is about to transfer it's input focus.
-     */
-    @Override
-    public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
-        if (!mCommandQueue.panelsEnabled()) {
-            return;
-        }
-
-        if (start) {
-            mShadeSurface.startWaitingForExpandGesture();
-        } else {
-            mShadeSurface.stopWaitingForExpandGesture(cancel, velocity);
-        }
-    }
-
-    @Override
-    public void onStatusBarTrackpadEvent(MotionEvent event) {
-        mShadeSurface.handleExternalTouch(event);
-    }
-
     private void onExpandedInvisible() {
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
         if (!mNotificationActivityStarterLazy.get().isCollapsingToShowActivityOverLockscreen()) {
@@ -1722,25 +1705,19 @@
         return mCommandQueue.panelsEnabled();
     }
 
-    @Override
-    public void showTransientUnchecked() {
-        if (!mTransientShown) {
-            mTransientShown = true;
+    private void onTransientShownChanged(boolean transientShown) {
+        if (transientShown) {
             mNoAnimationOnNextBarModeChange = true;
-            maybeUpdateBarMode();
         }
+        maybeUpdateBarMode();
     }
 
-    @Override
-    public void clearTransient() {
-        if (mTransientShown) {
-            mTransientShown = false;
-            maybeUpdateBarMode();
-        }
+    private void onStatusBarFullscreenChanged(boolean isWindowShown) {
+        maybeUpdateBarMode();
     }
 
     private void maybeUpdateBarMode() {
-        final int barMode = barMode(mTransientShown, mAppearance);
+        final int barMode = barMode(isTransientShown(), mAppearance);
         if (updateBarMode(barMode)) {
             mLightBarController.onStatusBarModeChanged(barMode);
             updateBubblesVisibility();
@@ -1758,8 +1735,10 @@
     }
 
     private @TransitionMode int barMode(boolean isTransient, int appearance) {
+        boolean isFullscreen = mStatusBarModeRepository.isInFullscreenMode().getValue();
         final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
-        if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) {
+        if (mOngoingCallController.hasOngoingCall() && isFullscreen) {
+            // Force show the status bar if there's an ongoing call.
             return MODE_SEMI_TRANSPARENT;
         } else if (isTransient) {
             return MODE_SEMI_TRANSPARENT;
@@ -2180,10 +2159,16 @@
             //  * When phone is unlocked: we still don't want to execute hiding of the keyguard
             //    as the animation could prepare 'fake AOD' interface (without actually
             //    transitioning to keyguard state) and this might reset the view states
+            // Log for b/290627350
+            Log.d(TAG, "!shouldBeKeyguard mStatusBarStateController.isKeyguardRequested() "
+                    + mStatusBarStateController.isKeyguardRequested() + " keyguardForDozing "
+                    + keyguardForDozing + " wakeAndUnlocking " + wakeAndUnlocking
+                    + " isWakingAndOccluded " + isWakingAndOccluded);
             if (!mScreenOffAnimationController.isKeyguardHideDelayed()
                     // If we're animating occluded, there's an activity launching over the keyguard
                     // UI. Wait to hide it until after the animation concludes.
                     && !mKeyguardViewMediator.isOccludeAnimationPlaying()) {
+                Log.d(TAG, "hideKeyguardImpl " + forceStateChange);
                 return hideKeyguardImpl(forceStateChange);
             }
         }
@@ -2460,10 +2445,7 @@
      */
     @Override
     public boolean shouldKeyguardHideImmediately() {
-        final boolean isScrimmedBouncer =
-                mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
-        final boolean isBouncerOverDream = isBouncerShowingOverDream();
-        return (isScrimmedBouncer || isBouncerOverDream);
+        return mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
     }
 
     private void showBouncerOrLockScreenIfKeyguard() {
@@ -2541,11 +2523,6 @@
         return mNavigationBarController.getNavigationBarView(mDisplayId);
     }
 
-    @Override
-    public boolean isOverviewEnabled() {
-        return mNavigationBarController.isOverviewEnabled(mDisplayId);
-    }
-
     /**
      * Propagation of the bouncer state, indicating that it's fully visible.
      */
@@ -2815,11 +2792,6 @@
         return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
     }
 
-    @Override
-    public void showScreenPinningRequest(int taskId, boolean allowCancel) {
-        mScreenPinningRequest.showPrompt(taskId, allowCancel);
-    }
-
     @Nullable
     @Override
     public Intent getEmergencyActionIntent() {
@@ -3160,15 +3132,10 @@
         return isBouncerShowing() && mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming();
     }
 
-    @Override
-    public boolean isBouncerShowingOverDream() {
-        return mBouncerShowingOverDream;
-    }
-
     // End Extra BaseStatusBarMethods.
 
     boolean isTransientShown() {
-        return mTransientShown;
+        return mStatusBarModeRepository.isTransientShown().getValue();
     }
 
     private void updateLightRevealScrimVisibility() {
@@ -3251,8 +3218,6 @@
             if (DEBUG) {
                 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
             }
-
-            mScreenPinningRequest.onConfigurationChanged();
         }
 
         @Override
@@ -3364,12 +3329,6 @@
                     updateReportRejectedTouchVisibility();
                     Trace.endSection();
                 }
-
-                @Override
-                public void onFullscreenStateChanged(boolean isFullscreen) {
-                    mIsFullscreen = isFullscreen;
-                    maybeUpdateBarMode();
-                }
             };
 
     private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index cdd410e..5de0d15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -20,7 +20,6 @@
 import android.util.AttributeSet
 import android.view.View
 import android.view.ViewGroup
-import android.view.ViewPropertyAnimator
 import android.view.WindowInsets
 import android.widget.FrameLayout
 import androidx.annotation.StringRes
@@ -32,7 +31,6 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.animation.requiresRemeasuring
 
 /**
  * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI
@@ -61,8 +59,10 @@
     }
 
     private var ambientIndicationArea: View? = null
+    private var keyguardIndicationArea: View? = null
     private var binding: KeyguardBottomAreaViewBinder.Binding? = null
     private var lockIconViewController: LockIconViewController? = null
+    private var isLockscreenLandscapeEnabled: Boolean = false
 
     /** Initializes the view. */
     @Deprecated("Deprecated as part of b/278057014")
@@ -116,21 +116,33 @@
         }
     }
 
+    fun setIsLockscreenLandscapeEnabled(isLockscreenLandscapeEnabled: Boolean) {
+        this.isLockscreenLandscapeEnabled = isLockscreenLandscapeEnabled
+    }
+
     override fun onFinishInflate() {
         super.onFinishInflate()
         ambientIndicationArea = findViewById(R.id.ambient_indication_container)
+        keyguardIndicationArea = findViewById(R.id.keyguard_indication_area)
     }
 
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
         binding?.onConfigurationChanged()
+
+        if (isLockscreenLandscapeEnabled) {
+            updateIndicationAreaBottomMargin()
+        }
     }
 
-    /** Returns a list of animators to use to animate the indication areas. */
-    @Deprecated("Deprecated as part of b/278057014")
-    val indicationAreaAnimators: List<ViewPropertyAnimator>
-        get() = checkNotNull(binding).getIndicationAreaAnimators()
-
+    private fun updateIndicationAreaBottomMargin() {
+        keyguardIndicationArea?.let {
+            val params = it.layoutParams as FrameLayout.LayoutParams
+            params.bottomMargin =
+                resources.getDimensionPixelSize(R.dimen.keyguard_indication_margin_bottom)
+            it.layoutParams = params
+        }
+    }
 
     override fun hasOverlappingRendering(): Boolean {
         return false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
index 3942dae..0bad47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -16,11 +16,20 @@
 
 package com.android.systemui.statusbar.phone
 
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
 import com.android.systemui.util.ViewController
 import javax.inject.Inject
 
-class KeyguardBottomAreaViewController @Inject constructor(view: KeyguardBottomAreaView) :
+class KeyguardBottomAreaViewController
+    @Inject constructor(view: KeyguardBottomAreaView, featureFlags: FeatureFlagsClassic) :
     ViewController<KeyguardBottomAreaView> (view) {
+
+    init {
+        view.setIsLockscreenLandscapeEnabled(
+                featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE))
+    }
+
     override fun onViewAttached() {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index be336e5..16413d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -45,6 +45,8 @@
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shade.ShadeViewStateProvider;
@@ -69,6 +71,8 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.SecureSettings;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -76,8 +80,6 @@
 
 import javax.inject.Inject;
 
-import kotlin.Unit;
-
 /** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
 public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
     private static final String TAG = "KeyguardStatusBarViewController";
@@ -111,6 +113,7 @@
     private final BiometricUnlockController mBiometricUnlockController;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final StatusBarContentInsetsProvider mInsetsProvider;
+    private final FeatureFlags mFeatureFlags;
     private final UserManager mUserManager;
     private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
     private final SecureSettings mSecureSettings;
@@ -283,6 +286,7 @@
             BiometricUnlockController biometricUnlockController,
             SysuiStatusBarStateController statusBarStateController,
             StatusBarContentInsetsProvider statusBarContentInsetsProvider,
+            FeatureFlags featureFlags,
             UserManager userManager,
             StatusBarUserChipViewModel userChipViewModel,
             SecureSettings secureSettings,
@@ -308,6 +312,7 @@
         mBiometricUnlockController = biometricUnlockController;
         mStatusBarStateController = statusBarStateController;
         mInsetsProvider = statusBarContentInsetsProvider;
+        mFeatureFlags = featureFlags;
         mUserManager = userManager;
         mStatusBarUserChipViewModel = userChipViewModel;
         mSecureSettings = secureSettings;
@@ -367,7 +372,19 @@
                     mView.findViewById(R.id.statusIcons), StatusBarLocation.KEYGUARD);
             mTintedIconManager.setBlockList(getBlockedIcons());
             mStatusBarIconController.addIconGroup(mTintedIconManager);
+        } else {
+            // In the old implementation, the keyguard status bar view is never detached and
+            // re-attached, so only calling #addIconGroup when the IconManager is first created was
+            // safe and correct.
+            // In the new scene framework implementation, the keyguard status bar view *is* detached
+            // whenever the shade is opened on top of lockscreen, and then re-attached when the
+            // shade is closed. So, we need to re-add the IconManager each time we're re-attached to
+            // get icon updates.
+            if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW)) {
+                mStatusBarIconController.addIconGroup(mTintedIconManager);
+            }
         }
+
         mSystemIconsContainer = mView.findViewById(R.id.system_icons);
         StatusOverlayHoverListener hoverListener = mStatusOverlayHoverListenerFactory
                 .createDarkAwareListener(mSystemIconsContainer, mView.darkChangeFlow());
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 3afbbfd..8d86d72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -22,7 +22,6 @@
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -472,17 +471,11 @@
         }
 
         if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+            // Show the keyguard views whenever we've told WM that the lockscreen is visible.
             mShadeViewController.postToView(() ->
                     collectFlow(
                         getViewRootImpl().getView(),
-                        combineFlows(
-                                mKeyguardTransitionInteractor.getFinishedKeyguardState(),
-                                mWmLockscreenVisibilityInteractor.get()
-                                        .getUsingKeyguardGoingAwayAnimation(),
-                                (finishedState, animating) ->
-                                        KeyguardInteractor.Companion.isKeyguardVisibleInState(
-                                                finishedState)
-                                                || animating),
+                        mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
                         this::consumeShowStatusBarKeyguardView));
         }
     }
@@ -565,7 +558,6 @@
                 && !mKeyguardStateController.isOccluded()
                 && !mKeyguardStateController.canDismissLockScreen()
                 && !bouncerIsAnimatingAway()
-                && !mShadeViewController.isUnlockHintRunning()
                 && !(mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED);
     }
 
@@ -636,8 +628,12 @@
     protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
         if (needsFullscreenBouncer() && !mDozing) {
             // The keyguard might be showing (already). So we need to hide it.
-            mCentralSurfaces.hideKeyguard();
-            mPrimaryBouncerInteractor.show(true);
+            if (!primaryBouncerIsShowing()) {
+                mCentralSurfaces.hideKeyguard();
+                mPrimaryBouncerInteractor.show(true);
+            } else {
+                Log.e(TAG, "Attempted to show the sim bouncer when it is already showing.");
+            }
         } else {
             mCentralSurfaces.showKeyguard();
             if (hideBouncerWhenShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
index c0269b8..829577b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.SysuiStatusBarStateController
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -43,7 +42,6 @@
 internal constructor(
     private val centralSurfaces: CentralSurfaces,
     private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
-    private val statusBarStateController: SysuiStatusBarStateController,
     private val lightBarController: LightBarController,
     dumpManager: DumpManager,
 ) : Dumpable, StatusBarBoundsProvider.BoundsChangeListener {
@@ -101,8 +99,6 @@
             appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)
 
         centralSurfaces.updateBubblesVisibility()
-        statusBarStateController.setSystemBarAttributes(
-            appearance, behavior, requestedVisibleTypes, packageName)
     }
 
     private fun modifyAppearanceIfNeeded(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index b3af91d..230bb58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -27,14 +27,16 @@
 import android.view.View
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.CoreStartable
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
 import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -42,8 +44,9 @@
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import java.io.PrintWriter
-import java.util.Optional
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -52,19 +55,19 @@
  */
 @SysUISingleton
 class OngoingCallController @Inject constructor(
+    @Application private val scope: CoroutineScope,
     private val context: Context,
     private val notifCollection: CommonNotifCollection,
-    private val ongoingCallFlags: OngoingCallFlags,
     private val systemClock: SystemClock,
     private val activityStarter: ActivityStarter,
     @Main private val mainExecutor: Executor,
     private val iActivityManager: IActivityManager,
     private val logger: OngoingCallLogger,
     private val dumpManager: DumpManager,
-    private val statusBarWindowController: Optional<StatusBarWindowController>,
-    private val swipeStatusBarAwayGestureHandler: Optional<SwipeStatusBarAwayGestureHandler>,
-    private val statusBarStateController: StatusBarStateController
-) : CallbackController<OngoingCallListener>, Dumpable {
+    private val statusBarWindowController: StatusBarWindowController,
+    private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
+    private val statusBarModeRepository: StatusBarModeRepository,
+) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
     private var isFullscreen: Boolean = false
     /** Non-null if there's an active call notification. */
     private var callNotificationInfo: CallNotificationInfo? = null
@@ -120,11 +123,15 @@
         }
     }
 
-    fun init() {
+    override fun start() {
         dumpManager.registerDumpable(this)
-        if (ongoingCallFlags.isStatusBarChipEnabled()) {
-            notifCollection.addCollectionListener(notifListener)
-            statusBarStateController.addCallback(statusBarStateListener)
+        notifCollection.addCollectionListener(notifListener)
+        scope.launch {
+            statusBarModeRepository.isInFullscreenMode.collect {
+                isFullscreen = it
+                updateChipClickListener()
+                updateGestureListening()
+            }
         }
     }
 
@@ -138,7 +145,7 @@
         this.chipView = chipView
         val backgroundView: OngoingCallBackgroundContainer? =
             chipView.findViewById(R.id.ongoing_call_chip_background)
-        backgroundView?.maxHeightFetcher = { statusBarWindowController.get().statusBarHeight }
+        backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
         if (hasOngoingCall()) {
             updateChip()
         }
@@ -197,9 +204,7 @@
 
             uidObserver.registerWithUid(currentCallNotificationInfo.uid)
             if (!currentCallNotificationInfo.statusBarSwipedAway) {
-                statusBarWindowController.ifPresent {
-                    it.setOngoingProcessRequiresStatusBarVisible(true)
-                }
+                statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
             }
             updateGestureListening()
             mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
@@ -217,23 +222,19 @@
 
     private fun updateChipClickListener() {
         if (callNotificationInfo == null) { return }
-        if (isFullscreen && !ongoingCallFlags.isInImmersiveChipTapEnabled()) {
-            chipView?.setOnClickListener(null)
-        } else {
-            val currentChipView = chipView
-            val backgroundView =
-                currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
-            val intent = callNotificationInfo?.intent
-            if (currentChipView != null && backgroundView != null && intent != null) {
-                currentChipView.setOnClickListener {
-                    logger.logChipClicked()
-                    activityStarter.postStartActivityDismissingKeyguard(
-                        intent,
-                        ActivityLaunchAnimator.Controller.fromView(
-                            backgroundView,
-                            InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
-                    )
-                }
+        val currentChipView = chipView
+        val backgroundView =
+            currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
+        val intent = callNotificationInfo?.intent
+        if (currentChipView != null && backgroundView != null && intent != null) {
+            currentChipView.setOnClickListener {
+                logger.logChipClicked()
+                activityStarter.postStartActivityDismissingKeyguard(
+                    intent,
+                    ActivityLaunchAnimator.Controller.fromView(
+                        backgroundView,
+                        InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+                )
             }
         }
     }
@@ -247,10 +248,10 @@
         if (callNotificationInfo == null ||
             callNotificationInfo?.statusBarSwipedAway == true ||
             !isFullscreen) {
-            swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+            swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
         } else {
-            swipeStatusBarAwayGestureHandler.ifPresent {
-                it.addOnGestureDetectedCallback(TAG) { _ -> onSwipeAwayGestureDetected() }
+            swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
+                onSwipeAwayGestureDetected()
             }
         }
     }
@@ -258,8 +259,8 @@
     private fun removeChip() {
         callNotificationInfo = null
         tearDownChipView()
-        statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
-        swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
+        statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+        swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
         mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
         uidObserver.unregister()
     }
@@ -283,20 +284,8 @@
     private fun onSwipeAwayGestureDetected() {
         if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
         callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
-        statusBarWindowController.ifPresent {
-            it.setOngoingProcessRequiresStatusBarVisible(false)
-        }
-        swipeStatusBarAwayGestureHandler.ifPresent {
-            it.removeOnGestureDetectedCallback(TAG)
-        }
-    }
-
-    private val statusBarStateListener = object : StatusBarStateController.StateListener {
-        override fun onFullscreenStateChanged(isFullscreen: Boolean) {
-            this@OngoingCallController.isFullscreen = isFullscreen
-            updateChipClickListener()
-            updateGestureListening()
-        }
+        statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+        swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
     }
 
     private data class CallNotificationInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
deleted file mode 100644
index fcfcb8f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.ongoingcall
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import javax.inject.Inject
-
-@SysUISingleton
-class OngoingCallFlags @Inject constructor(private val featureFlags: FeatureFlags) {
-
-    fun isStatusBarChipEnabled(): Boolean =
-            featureFlags.isEnabled(Flags.ONGOING_CALL_STATUS_BAR_CHIP)
-
-    fun isInImmersiveEnabled(): Boolean = isStatusBarChipEnabled()
-            && featureFlags.isEnabled(Flags.ONGOING_CALL_IN_IMMERSIVE)
-
-    fun isInImmersiveChipTapEnabled(): Boolean = isInImmersiveEnabled()
-            && featureFlags.isEnabled(Flags.ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP)
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
index 327dd8d..1f076ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
@@ -32,17 +32,13 @@
     val iconId: Int?
     val icon: QSTile.Icon?
 
-    fun applyTo(state: QSTile.SignalState, context: Context) {
+    fun applyTo(state: QSTile.BooleanState, context: Context) {
         if (secondaryLabel != null) {
             state.secondaryLabel = secondaryLabel.loadText(context)
         } else {
             state.secondaryLabel = secondaryTitle
         }
 
-        // inout indicators are unused
-        state.activityIn = false
-        state.activityOut = false
-
         // To support both SignalDrawable and other icons, give priority to icons over IDs
         if (icon != null) {
             state.icon = icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
index d5f2d21..67a8e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
@@ -20,7 +20,6 @@
 import android.content.res.Configuration
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.LargeScreenUtils
 import javax.inject.Inject
 
 /**
@@ -30,9 +29,10 @@
  */
 @SysUISingleton
 class RemoteInputQuickSettingsDisabler @Inject constructor(
-    private val context: Context,
-    private val commandQueue: CommandQueue,
-    configController: ConfigurationController
+        private val context: Context,
+        private val commandQueue: CommandQueue,
+        private val splitShadeStateController: SplitShadeStateController,
+        configController: ConfigurationController
 ) : ConfigurationController.ConfigurationListener {
 
     private var remoteInputActive = false
@@ -43,7 +43,7 @@
         isLandscape =
             context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
         shouldUseSplitNotificationShade =
-                LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+                splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
         configController.addCallback(this)
     }
 
@@ -74,7 +74,8 @@
             needToRecompute = true
         }
 
-        val newSplitShadeFlag = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
+        val newSplitShadeFlag = splitShadeStateController
+                .shouldUseSplitNotificationShade(context.resources)
         if (newSplitShadeFlag != shouldUseSplitNotificationShade) {
             shouldUseSplitNotificationShade = newSplitShadeFlag
             needToRecompute = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
new file mode 100644
index 0000000..e71c972
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.policy
+
+import android.content.res.Resources
+import com.android.systemui.R
+
+/**
+ * Fake SplitShadeStateController
+ *
+ * Identical behaviour to legacy implementation (that used LargeScreenUtils.kt) I.E., behaviour
+ * based solely on resources, no extra flag logic.
+ */
+class ResourcesSplitShadeStateController : SplitShadeStateController {
+    override fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
+        return resources.getBoolean(R.bool.config_use_split_notification_shade)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateController.kt
new file mode 100644
index 0000000..f64d4c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateController.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.policy
+
+import android.content.res.Resources
+
+/** Source of truth for split shade state: should or should not use split shade. */
+interface SplitShadeStateController {
+    /** Returns true if the device should use the split notification shade. */
+    fun shouldUseSplitNotificationShade(resources: Resources): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt
new file mode 100644
index 0000000..ab4a8af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerImpl.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.policy
+
+import android.content.res.Resources
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/**
+ * Source of truth for split shade state: should or should not use split shade based on orientation,
+ * screen width, and flags.
+ */
+@SysUISingleton
+class SplitShadeStateControllerImpl @Inject constructor(private val featureFlags: FeatureFlags) :
+    SplitShadeStateController {
+    /**
+     * Returns true if the device should use the split notification shade. Based on orientation,
+     * screen width, and flags.
+     */
+    override fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
+        return (resources.getBoolean(R.bool.config_use_split_notification_shade) ||
+            (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) &&
+                resources.getBoolean(R.bool.force_config_use_split_notification_shade)))
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index dabdcc5..39cdfa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -169,6 +169,7 @@
     @Override
     public void addCallback(@NonNull Callback callback) {
         synchronized (mCallbacksLock) {
+            Log.d(TAG, "Added callback " + callback.getClass());
             mCallbacks.add(callback);
         }
     }
@@ -176,6 +177,7 @@
     @Override
     public void removeCallback(@NonNull Callback callback) {
         synchronized (mCallbacksLock) {
+            Log.d(TAG, "Removed callback " + callback.getClass());
             mCallbacks.remove(callback);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index de07a48..927024f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -60,6 +60,8 @@
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
+import com.android.systemui.statusbar.policy.SplitShadeStateControllerImpl;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.WalletController;
@@ -113,6 +115,11 @@
 
     /** */
     @Binds
+    SplitShadeStateController provideSplitShadeStateController(
+            SplitShadeStateControllerImpl splitShadeStateControllerImpl);
+
+    /** */
+    @Binds
     HotspotController provideHotspotController(HotspotControllerImpl controllerImpl);
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt b/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
index 8b29310..9b241a7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
@@ -4,16 +4,6 @@
 import com.android.systemui.R
 
 object LargeScreenUtils {
-
-    /**
-     * Returns true if the device should use the split notification shade, based on orientation and
-     * screen width.
-     */
-    @JvmStatic
-    fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
-        return resources.getBoolean(R.bool.config_use_split_notification_shade)
-    }
-
     /**
      * Returns true if we should use large screen shade header:
      * [com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController]
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 81d04d4..6fd0a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.wallet.ui;
 
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -47,10 +49,10 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /** Controller for the wallet card carousel screen. */
 public class WalletScreenController implements
@@ -126,22 +128,11 @@
             return;
         }
         Log.i(TAG, "Successfully retrieved wallet cards.");
-        List<WalletCard> walletCards = response.getWalletCards();
+        List<WalletCard> walletCards = getPaymentCards(response.getWalletCards());
 
-        boolean allUnknown = true;
-        for (WalletCard card : walletCards) {
-            if (card.getCardType() != WalletCard.CARD_TYPE_UNKNOWN) {
-                allUnknown = false;
-                break;
-            }
-        }
-
-        List<WalletCardViewInfo> paymentCardData = new ArrayList<>();
-        for (WalletCard card : walletCards) {
-            if (allUnknown || card.getCardType() == WalletCard.CARD_TYPE_PAYMENT) {
-                paymentCardData.add(new QAWalletCardViewInfo(mContext, card));
-            }
-        }
+        List<WalletCardViewInfo> paymentCardData = walletCards.stream().map(
+                card -> new QAWalletCardViewInfo(mContext, card)
+        ).collect(Collectors.toList());
 
         // Get on main thread for UI updates.
         mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
new file mode 100644
index 0000000..077b80b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+
+/**
+ * Filters wallet cards to only those of [WalletCard.CARD_TYPE_PAYMENT], or returns all cards if
+ * they are all of [WalletCard.CARD_TYPE_UNKNOWN] (maintaining pre-U behavior). Used by the wallet
+ * card carousel, quick settings tile, and lock screen.
+ */
+fun getPaymentCards(walletCards: List<WalletCard>): List<WalletCard> {
+    val atLeastOneKnownCardType = walletCards.any { it.cardType != WalletCard.CARD_TYPE_UNKNOWN }
+
+    return if (atLeastOneKnownCardType) {
+        walletCards.filter { it.cardType == WalletCard.CARD_TYPE_PAYMENT }
+    } else {
+        walletCards
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6fafcd5..897c4da 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -61,6 +61,7 @@
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.sysui.ShellInterface;
 
@@ -109,6 +110,7 @@
     private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<OneHanded> mOneHandedOptional;
     private final Optional<DesktopMode> mDesktopModeOptional;
+    private final Optional<RecentTasks> mRecentTasksOptional;
 
     private final CommandQueue mCommandQueue;
     private final ConfigurationController mConfigurationController;
@@ -172,6 +174,7 @@
             Optional<SplitScreen> splitScreenOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<DesktopMode> desktopMode,
+            Optional<RecentTasks> recentTasks,
             CommandQueue commandQueue,
             ConfigurationController configurationController,
             KeyguardStateController keyguardStateController,
@@ -195,6 +198,7 @@
         mSplitScreenOptional = splitScreenOptional;
         mOneHandedOptional = oneHandedOptional;
         mDesktopModeOptional = desktopMode;
+        mRecentTasksOptional = recentTasks;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mUserTracker = userTracker;
         mDisplayTracker = displayTracker;
@@ -220,6 +224,7 @@
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
         mDesktopModeOptional.ifPresent(this::initDesktopMode);
+        mRecentTasksOptional.ifPresent(this::initRecentTasks);
 
         mNoteTaskInitializer.initialize();
     }
@@ -351,6 +356,12 @@
                 }, mSysUiMainExecutor);
     }
 
+    @VisibleForTesting
+    void initRecentTasks(RecentTasks recentTasks) {
+        recentTasks.addAnimationStateListener(mSysUiMainExecutor,
+                mCommandQueue::onRecentsAnimationStateChanged);
+    }
+
     @Override
     public boolean isDumpCritical() {
         // Dump can't be critical because the shell has to dump on the main thread for
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index e1b608f..ee67348 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.mockito.whenever
 import org.junit.Before
@@ -67,6 +68,7 @@
     @Mock
     private lateinit var mKeyguardMessageAreaController:
         KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+    @Mock private lateinit var postureController: DevicePostureController
 
     private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
 
@@ -89,6 +91,7 @@
         `when`(keyguardPasswordView.resources).thenReturn(context.resources)
         val fakeFeatureFlags = FakeFeatureFlags()
         fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+        fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
         keyguardPasswordViewController =
             KeyguardPasswordViewController(
                 keyguardPasswordView,
@@ -104,6 +107,7 @@
                 mContext.resources,
                 falsingCollector,
                 keyguardViewController,
+                postureController,
                 fakeFeatureFlags
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 93048a5..0ef9f45 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -94,9 +94,10 @@
             .thenReturn(mKeyguardMessageAreaController)
         fakeFeatureFlags = FakeFeatureFlags()
         fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
+        fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
         mKeyguardPatternView =
             View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView
-
+        mKeyguardPatternView.setIsLockScreenLandscapeEnabled(false)
         mKeyguardPatternViewController =
             KeyguardPatternViewController(
                 mKeyguardPatternView,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 33d4097..a9f044c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -123,7 +123,6 @@
     private fun constructPinViewController(
         mKeyguardPinView: KeyguardPINView
     ): KeyguardPinViewController {
-        mKeyguardPinView.setIsLockScreenLandscapeEnabled(false)
         return KeyguardPinViewController(
             mKeyguardPinView,
             keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 7c1861e..929604b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -60,6 +60,7 @@
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.UserSwitcherController
@@ -140,6 +141,7 @@
     @Mock private lateinit var userInteractor: UserInteractor
     @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+    @Mock private lateinit var postureController: DevicePostureController
 
     @Captor
     private lateinit var swipeListenerArgumentCaptor:
@@ -197,6 +199,7 @@
         featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false)
         featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
         featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
+        featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
 
         keyguardPasswordViewController =
             KeyguardPasswordViewController(
@@ -213,6 +216,7 @@
                 mock(),
                 null,
                 keyguardViewController,
+                postureController,
                 featureFlags
             )
 
@@ -806,7 +810,8 @@
                 ObservableTransitionState.Transition(
                     SceneKey.Lockscreen,
                     SceneKey.Bouncer,
-                    flowOf(.5f)
+                    flowOf(.5f),
+                    false,
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -818,7 +823,12 @@
             // keyguard.
             sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
             sceneTransitionStateFlow.value =
-                ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
+                ObservableTransitionState.Transition(
+                    SceneKey.Bouncer,
+                    SceneKey.Gone,
+                    flowOf(.5f),
+                    false
+                )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
             sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
@@ -830,7 +840,12 @@
             clearInvocations(viewMediatorCallback)
             sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason")
             sceneTransitionStateFlow.value =
-                ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f))
+                ObservableTransitionState.Transition(
+                    SceneKey.Gone,
+                    SceneKey.Bouncer,
+                    flowOf(.5f),
+                    false
+                )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
             sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
@@ -843,7 +858,12 @@
             underTest.onViewDetached()
             sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
             sceneTransitionStateFlow.value =
-                ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
+                ObservableTransitionState.Transition(
+                    SceneKey.Bouncer,
+                    SceneKey.Gone,
+                    flowOf(.5f),
+                    false
+                )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
             sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
@@ -856,7 +876,8 @@
                 ObservableTransitionState.Transition(
                     SceneKey.Gone,
                     SceneKey.Lockscreen,
-                    flowOf(.5f)
+                    flowOf(.5f),
+                    false,
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -872,7 +893,8 @@
                 ObservableTransitionState.Transition(
                     SceneKey.Lockscreen,
                     SceneKey.Gone,
-                    flowOf(.5f)
+                    flowOf(.5f),
+                    false,
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 562d3a5..1d7c328 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -29,6 +29,7 @@
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -38,11 +39,17 @@
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -139,6 +146,7 @@
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.FakeDisplayTracker;
@@ -172,7 +180,6 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Random;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -276,6 +283,8 @@
     private TaskStackChangeListeners mTaskStackChangeListeners;
     @Mock
     private IActivityTaskManager mActivityTaskManager;
+    @Mock
+    private WakefulnessLifecycle mWakefulness;
 
     private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
     private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
@@ -1363,7 +1372,7 @@
         assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
         assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
 
-        mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, new CountDownLatch(0));
+        mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {});
         assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
         assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
     }
@@ -2999,8 +3008,57 @@
         // THEN face listening is stopped.
         verify(faceCancel).cancel();
         verify(callback).onBiometricRunningStateChanged(
-                eq(false), eq(BiometricSourceType.FACE)); // beverlyt
+                eq(false), eq(BiometricSourceType.FACE));
+    }
 
+    @Test
+    public void onDisplayOff_whileAsleep_doesNotStopFaceAuth() throws RemoteException {
+        enableStopFaceAuthOnDisplayOff();
+        when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_ASLEEP);
+
+        // GIVEN device is listening for face
+        mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+        verifyFaceAuthenticateCall();
+
+        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN the default display state changes to OFF
+        triggerDefaultDisplayStateChangeToOff();
+
+        // THEN face listening is NOT stopped.
+        verify(faceCancel, never()).cancel();
+        verify(callback, never()).onBiometricRunningStateChanged(
+                eq(false), eq(BiometricSourceType.FACE));
+    }
+
+    @Test
+    public void onDisplayOff_whileWaking_doesNotStopFaceAuth() throws RemoteException {
+        enableStopFaceAuthOnDisplayOff();
+        when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_WAKING);
+
+        // GIVEN device is listening for face
+        mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+        verifyFaceAuthenticateCall();
+
+        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN the default display state changes to OFF
+        triggerDefaultDisplayStateChangeToOff();
+
+        // THEN face listening is NOT stopped.
+        verify(faceCancel, never()).cancel();
+        verify(callback, never()).onBiometricRunningStateChanged(
+                eq(false), eq(BiometricSourceType.FACE));
     }
 
     private void triggerDefaultDisplayStateChangeToOn() {
@@ -3307,6 +3365,7 @@
         clearInvocations(mStatusBarStateController);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
         setupBiometrics(mKeyguardUpdateMonitor);
+        when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
         assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1);
     }
 
@@ -3387,7 +3446,8 @@
                     mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
                     mFaceWakeUpTriggersConfig, mDevicePostureController,
                     Optional.of(mInteractiveToAuthProvider),
-                    mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker);
+                    mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
+                    mWakefulness);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 09ff546..0e4b3c9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -19,6 +19,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON;
 
@@ -146,6 +147,7 @@
         mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
         mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
         mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
+        mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
         mUnderTest = new LockIconViewController(
                 mStatusBarStateController,
                 mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index f9830b1..0b0410a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -59,6 +59,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
@@ -108,6 +109,7 @@
 
     private AppOpsControllerImpl mController;
     private TestableLooper mTestableLooper;
+    private final FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
 
     private String mExemptedRolePkgName;
 
@@ -139,6 +141,7 @@
         mController = new AppOpsControllerImpl(
                 mContext,
                 mTestableLooper.getLooper(),
+                mBgExecutor,
                 mDumpManager,
                 mAudioManager,
                 mSensorPrivacyController,
@@ -150,6 +153,8 @@
     @Test
     public void testOnlyListenForFewOps() {
         mController.setListening(true);
+        mBgExecutor.runAllReady();
+
         verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
         verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
         verify(mSensorPrivacyController, times(1)).addCallback(mController);
@@ -158,6 +163,7 @@
     @Test
     public void testStopListening() {
         mController.setListening(false);
+        mBgExecutor.runAllReady();
         verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
         verify(mDispatcher, times(1)).unregisterReceiver(mController);
         verify(mSensorPrivacyController, times(1)).removeCallback(mController);
@@ -169,6 +175,7 @@
                 .thenReturn(List.of());
 
         mController.setListening(true);
+        mBgExecutor.runAllReady();
 
         assertThat(mController.getActiveAppOps()).isEmpty();
     }
@@ -186,6 +193,7 @@
 
         // WHEN we start listening
         mController.setListening(true);
+        mBgExecutor.runAllReady();
 
         // THEN the active list has the op
         List<AppOpItem> list = mController.getActiveAppOps();
@@ -218,6 +226,7 @@
 
         // WHEN we start listening
         mController.setListening(true);
+        mBgExecutor.runAllReady();
 
         // THEN the active list has the ops
         List<AppOpItem> list = mController.getActiveAppOps();
@@ -265,6 +274,7 @@
 
         // WHEN we start listening
         mController.setListening(true);
+        mBgExecutor.runAllReady();
 
         // THEN the active list has the ops
         List<AppOpItem> list = mController.getActiveAppOps();
@@ -304,6 +314,7 @@
 
         // WHEN we start listening
         mController.setListening(true);
+        mBgExecutor.runAllReady();
 
         // THEN the active list has the ops
         List<AppOpItem> list = mController.getActiveAppOps();
@@ -341,6 +352,7 @@
         mController.addCallback(
                 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
                 mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
 
         // THEN the callback is notified of the current active ops it cares about
@@ -366,11 +378,14 @@
         mController.addCallback(
                 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
                 mCallback);
+        mBgExecutor.runAllReady();
+
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
         mTestableLooper.processAllMessages();
+
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
     }
@@ -383,6 +398,7 @@
     public void addCallback_partialIncludedCode() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
                 AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mBgExecutor.runAllReady();
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpActiveChanged(
@@ -400,9 +416,12 @@
     @Test
     public void addCallback_notIncludedCode() {
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mBgExecutor.runAllReady();
+
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
+
         verify(mCallback, never()).onActiveStateChanged(
                 anyInt(), anyInt(), anyString(), anyBoolean());
     }
@@ -410,7 +429,10 @@
     @Test
     public void removeCallback_sameCode() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mBgExecutor.runAllReady();
+
         mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mBgExecutor.runAllReady();
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
@@ -421,7 +443,10 @@
     @Test
     public void addCallback_notSameCode() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mBgExecutor.runAllReady();
+
         mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
+        mBgExecutor.runAllReady();
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
@@ -484,6 +509,8 @@
         assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
 
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mBgExecutor.runAllReady();
+
         mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
                 TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
 
@@ -506,6 +533,7 @@
         mController.setBGHandler(mMockHandler);
 
         mController.setListening(true);
+        mBgExecutor.runAllReady();
         mController.onOpActiveChanged(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID,
                 TEST_PACKAGE_NAME, true);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
@@ -513,6 +541,7 @@
         assertFalse(mController.getActiveAppOps().isEmpty());
 
         mController.setListening(false);
+        mBgExecutor.runAllReady();
 
         verify(mMockHandler).removeCallbacksAndMessages(null);
         assertTrue(mController.getActiveAppOps().isEmpty());
@@ -583,6 +612,7 @@
         TestHandler testHandler = new TestHandler(mTestableLooper.getLooper());
 
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mBgExecutor.runAllReady();
         mController.setBGHandler(testHandler);
 
         mController.onOpActiveChanged(
@@ -616,6 +646,7 @@
     @Test
     public void testNotedNotRemovedAfterActive() {
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mBgExecutor.runAllReady();
 
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -645,6 +676,7 @@
     @Test
     public void testNotedAndActiveOnlyOneCall() {
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mBgExecutor.runAllReady();
 
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -660,6 +692,7 @@
     @Test
     public void testActiveAndNotedOnlyOneCall() {
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mBgExecutor.runAllReady();
 
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
@@ -675,6 +708,7 @@
     @Test
     public void testPausedRecordingIsRetrievedOnCreation() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
 
         mController.onOpActiveChanged(
@@ -688,6 +722,7 @@
     @Test
     public void testPausedRecordingFilteredOut() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
 
         mController.onOpActiveChanged(
@@ -700,6 +735,7 @@
     @Test
     public void testPausedPhoneCallMicrophoneFilteredOut() {
         mController.addCallback(new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
 
         mController.onOpActiveChanged(
@@ -715,6 +751,7 @@
                 AppOpsManager.OP_RECORD_AUDIO,
                 AppOpsManager.OP_CAMERA
         }, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
 
         mController.onOpActiveChanged(
@@ -751,6 +788,7 @@
         // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
         // verify the micOp is the only active op returned.
         mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
                 AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -779,6 +817,7 @@
         // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
         // verify the micOp is the only active op returned.
         mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
                 AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -807,6 +846,7 @@
         // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
         // verify the micOp is the only active op returned.
         mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
                 AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -835,6 +875,7 @@
         // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
         // verify the micOp is the only active op returned.
         mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
                 AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -859,6 +900,7 @@
     public void testCameraFilteredWhenCameraDisabled() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
                 mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -892,6 +934,7 @@
         mController.addCallback(
                 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_CAMERA},
                 mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
                 AppOpsManager.OPSTR_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
@@ -922,6 +965,7 @@
 
     private void verifyUnPausedSentActive(int micOpCode) {
         mController.addCallback(new int[]{micOpCode}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID,
                 TEST_PACKAGE_NAME, true);
@@ -936,6 +980,7 @@
 
     private void verifyAudioPausedSentInactive(int micOpCode) {
         mController.addCallback(new int[]{micOpCode}, mCallback);
+        mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID_OTHER,
                 TEST_PACKAGE_NAME, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 7775a05..969a011 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -41,7 +41,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
@@ -109,7 +109,7 @@
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val biometricPromptRepository = FakePromptRepository()
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
-    private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
     private val credentialInteractor = FakeCredentialInteractor()
     private val bpCredentialInteractor = PromptCredentialInteractor(
         Dispatchers.Main.immediate,
@@ -141,7 +141,7 @@
                     testScope.backgroundScope,
                     mContext,
                     fakeExecutor,
-                    rearDisplayStateRepository,
+                    displayStateRepository,
                     displayRepository,
             )
     }
@@ -520,6 +520,7 @@
             displayStateInteractor,
             promptSelectorInteractor,
             vibrator,
+            context,
             featureFlags
         ),
         { credentialViewModel },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 2bb3785..8fc63b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
 import com.android.systemui.user.data.repository.FakeUserRepository
@@ -72,6 +73,7 @@
         SharedNotificationContainerInteractor(
             configurationRepository,
             mContext,
+            ResourcesSplitShadeStateController()
         )
 
     private lateinit var detector: AuthDialogPanelInteractionDetector
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 3ebc2d6..17928a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -56,7 +56,7 @@
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.SysuiTestableContext
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -117,7 +117,7 @@
     @Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
 
     private lateinit var displayRepository: FakeDisplayRepository
-    private lateinit var rearDisplayStateRepository: FakeRearDisplayStateRepository
+    private lateinit var displayStateRepository: FakeDisplayStateRepository
     private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     private lateinit var displayStateInteractor: DisplayStateInteractor
@@ -145,7 +145,7 @@
     @Before
     fun setup() {
         displayRepository = FakeDisplayRepository()
-        rearDisplayStateRepository = FakeRearDisplayStateRepository()
+        displayStateRepository = FakeDisplayStateRepository()
         keyguardBouncerRepository = FakeKeyguardBouncerRepository()
         alternateBouncerInteractor =
             AlternateBouncerInteractor(
@@ -161,7 +161,7 @@
                 testScope.backgroundScope,
                 context,
                 executor,
-                rearDisplayStateRepository,
+                displayStateRepository,
                 displayRepository,
             )
 
@@ -273,7 +273,7 @@
                 TestCoroutineScope(),
                 dumpManager
             )
-        rearDisplayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)
+        displayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)
 
         overlayController =
             ArgumentCaptor.forClass(ISidefpsController::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
new file mode 100644
index 0000000..c9c46cb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.DisplayStateRepository
+import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.same
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
+private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DisplayStateRepositoryTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var deviceStateManager: DeviceStateManager
+    @Mock private lateinit var displayManager: DisplayManager
+    @Mock private lateinit var handler: Handler
+    @Mock private lateinit var display: Display
+    private lateinit var underTest: DisplayStateRepository
+
+    private val testScope = TestScope(StandardTestDispatcher())
+    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+    @Captor
+    private lateinit var displayListenerCaptor: ArgumentCaptor<DisplayManager.DisplayListener>
+
+    @Before
+    fun setUp() {
+        val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
+        mContext.orCreateTestableResources.addOverride(
+            com.android.internal.R.array.config_rearDisplayDeviceStates,
+            rearDisplayDeviceStates
+        )
+
+        mContext = spy(mContext)
+        whenever(mContext.display).thenReturn(display)
+
+        underTest =
+            DisplayStateRepositoryImpl(
+                testScope.backgroundScope,
+                mContext,
+                deviceStateManager,
+                displayManager,
+                handler,
+                fakeExecutor
+            )
+    }
+
+    @Test
+    fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() =
+        testScope.runTest {
+            val isInRearDisplayMode by collectLastValue(underTest.isInRearDisplayMode)
+            runCurrent()
+
+            val callback = deviceStateManager.captureCallback()
+
+            callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
+            assertThat(isInRearDisplayMode).isFalse()
+
+            callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
+            assertThat(isInRearDisplayMode).isTrue()
+        }
+
+    @Test
+    fun updatesCurrentRotation_whenDisplayStateChanges() =
+        testScope.runTest {
+            val currentRotation by collectLastValue(underTest.currentRotation)
+            runCurrent()
+
+            verify(displayManager)
+                .registerDisplayListener(
+                    displayListenerCaptor.capture(),
+                    same(handler),
+                    eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
+                )
+
+            whenever(display.getDisplayInfo(any())).then {
+                val info = it.getArgument<DisplayInfo>(0)
+                info.rotation = Surface.ROTATION_90
+                return@then true
+            }
+            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_90)
+            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_90)
+
+            whenever(display.getDisplayInfo(any())).then {
+                val info = it.getArgument<DisplayInfo>(0)
+                info.rotation = Surface.ROTATION_180
+                return@then true
+            }
+            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_180)
+            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_180)
+        }
+}
+
+private fun DeviceStateManager.captureCallback() =
+    withArgCaptor<DeviceStateManager.DeviceStateCallback> {
+        verify(this@captureCallback).registerCallback(any(), capture())
+    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
deleted file mode 100644
index dfe8d36..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.keyguard.data.repository
-
-import android.hardware.devicestate.DeviceStateManager
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-
-private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
-private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class RearDisplayStateRepositoryTest : SysuiTestCase() {
-    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
-    @Mock private lateinit var deviceStateManager: DeviceStateManager
-    private lateinit var underTest: RearDisplayStateRepository
-
-    private val testScope = TestScope(StandardTestDispatcher())
-    private val fakeExecutor = FakeExecutor(FakeSystemClock())
-
-    @Captor
-    private lateinit var callbackCaptor: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
-
-    @Before
-    fun setUp() {
-        val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.array.config_rearDisplayDeviceStates,
-            rearDisplayDeviceStates
-        )
-
-        underTest =
-            RearDisplayStateRepositoryImpl(
-                testScope.backgroundScope,
-                mContext,
-                deviceStateManager,
-                fakeExecutor
-            )
-    }
-
-    @Test
-    fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() =
-        testScope.runTest {
-            val isInRearDisplayMode = collectLastValue(underTest.isInRearDisplayMode)
-            runCurrent()
-
-            val callback = deviceStateManager.captureCallback()
-
-            callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
-            assertThat(isInRearDisplayMode()).isFalse()
-
-            callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
-            assertThat(isInRearDisplayMode()).isTrue()
-        }
-}
-
-private fun DeviceStateManager.captureCallback() =
-    withArgCaptor<DeviceStateManager.DeviceStateCallback> {
-        verify(this@captureCallback).registerCallback(any(), capture())
-    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
index 524f254..bf6caad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
@@ -3,7 +3,8 @@
 import android.view.Display
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.display.data.repository.display
@@ -37,7 +38,7 @@
 
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val testScope = TestScope(StandardTestDispatcher())
-    private lateinit var rearDisplayStateRepository: FakeRearDisplayStateRepository
+    private lateinit var displayStateRepository: FakeDisplayStateRepository
     private lateinit var displayRepository: FakeDisplayRepository
 
     @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
@@ -45,14 +46,14 @@
 
     @Before
     fun setup() {
-        rearDisplayStateRepository = FakeRearDisplayStateRepository()
+        displayStateRepository = FakeDisplayStateRepository()
         displayRepository = FakeDisplayRepository()
         interactor =
             DisplayStateInteractorImpl(
                 testScope.backgroundScope,
                 mContext,
                 fakeExecutor,
-                rearDisplayStateRepository,
+                displayStateRepository,
                 displayRepository,
             )
         interactor.setScreenSizeFoldProvider(screenSizeFoldProvider)
@@ -61,27 +62,39 @@
     @Test
     fun isInRearDisplayModeChanges() =
         testScope.runTest {
-            val isInRearDisplayMode = collectLastValue(interactor.isInRearDisplayMode)
+            val isInRearDisplayMode by collectLastValue(interactor.isInRearDisplayMode)
 
-            rearDisplayStateRepository.setIsInRearDisplayMode(false)
-            assertThat(isInRearDisplayMode()).isFalse()
+            displayStateRepository.setIsInRearDisplayMode(false)
+            assertThat(isInRearDisplayMode).isFalse()
 
-            rearDisplayStateRepository.setIsInRearDisplayMode(true)
-            assertThat(isInRearDisplayMode()).isTrue()
+            displayStateRepository.setIsInRearDisplayMode(true)
+            assertThat(isInRearDisplayMode).isTrue()
+        }
+
+    @Test
+    fun currentRotationChanges() =
+        testScope.runTest {
+            val currentRotation by collectLastValue(interactor.currentRotation)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_180)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_90)
         }
 
     @Test
     fun isFoldedChanges() =
         testScope.runTest {
-            val isFolded = collectLastValue(interactor.isFolded)
+            val isFolded by collectLastValue(interactor.isFolded)
             runCurrent()
             val callback = screenSizeFoldProvider.captureCallback()
 
             callback.onFoldUpdated(isFolded = true)
-            assertThat(isFolded()).isTrue()
+            assertThat(isFolded).isTrue()
 
             callback.onFoldUpdated(isFolded = false)
-            assertThat(isFolded()).isFalse()
+            assertThat(isFolded).isFalse()
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
index b3964b6..fd86486 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
@@ -4,9 +4,9 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -43,7 +43,7 @@
     private lateinit var displayRepository: FakeDisplayRepository
     private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
     private lateinit var promptRepository: FakePromptRepository
-    private lateinit var rearDisplayStateRepository: FakeRearDisplayStateRepository
+    private lateinit var displayStateRepository: FakeDisplayStateRepository
 
     private val testScope = TestScope(StandardTestDispatcher())
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
@@ -57,7 +57,7 @@
         displayRepository = FakeDisplayRepository()
         fingerprintRepository = FakeFingerprintPropertyRepository()
         promptRepository = FakePromptRepository()
-        rearDisplayStateRepository = FakeRearDisplayStateRepository()
+        displayStateRepository = FakeDisplayStateRepository()
 
         promptSelectorInteractor =
             PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
@@ -66,7 +66,7 @@
                 testScope.backgroundScope,
                 mContext,
                 fakeExecutor,
-                rearDisplayStateRepository,
+                displayStateRepository,
                 displayRepository,
             )
         viewModel = PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor)
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 5834e31..ca6df40 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
@@ -24,9 +24,9 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -82,7 +82,7 @@
 
     private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
     private lateinit var promptRepository: FakePromptRepository
-    private lateinit var rearDisplayStateRepository: FakeRearDisplayStateRepository
+    private lateinit var displayStateRepository: FakeDisplayStateRepository
     private lateinit var displayRepository: FakeDisplayRepository
     private lateinit var displayStateInteractor: DisplayStateInteractor
 
@@ -94,21 +94,22 @@
     fun setup() {
         fingerprintRepository = FakeFingerprintPropertyRepository()
         promptRepository = FakePromptRepository()
-        rearDisplayStateRepository = FakeRearDisplayStateRepository()
+        displayStateRepository = FakeDisplayStateRepository()
         displayRepository = FakeDisplayRepository()
         displayStateInteractor =
             DisplayStateInteractorImpl(
                 testScope.backgroundScope,
                 mContext,
                 fakeExecutor,
-                rearDisplayStateRepository,
+                displayStateRepository,
                 displayRepository,
             )
         selector =
             PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
         selector.resetPrompt()
 
-        viewModel = PromptViewModel(displayStateInteractor, selector, vibrator, featureFlags)
+        viewModel =
+            PromptViewModel(displayStateInteractor, selector, vibrator, mContext, featureFlags)
         featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 3c5212a..8ce738c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.shared.model.SceneKey
@@ -30,6 +31,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -67,6 +69,9 @@
             isInputEnabled = MutableStateFlow(true).asStateFlow(),
         )
 
+    private val containerSize = 90 // px
+    private val dotSize = 30 // px
+
     @Before
     fun setUp() {
         overrideResource(R.string.keyguard_enter_your_pattern, ENTER_YOUR_PATTERN)
@@ -80,13 +85,7 @@
             val message by collectLastValue(bouncerViewModel.message)
             val selectedDots by collectLastValue(underTest.selectedDots)
             val currentDot by collectLastValue(underTest.currentDot)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pattern
-            )
-            utils.authenticationRepository.setUnlocked(false)
-            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+            transitionToPatternBouncer()
 
             underTest.onShown()
 
@@ -103,13 +102,7 @@
             val message by collectLastValue(bouncerViewModel.message)
             val selectedDots by collectLastValue(underTest.selectedDots)
             val currentDot by collectLastValue(underTest.currentDot)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pattern
-            )
-            utils.authenticationRepository.setUnlocked(false)
-            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+            transitionToPatternBouncer()
             underTest.onShown()
             runCurrent()
 
@@ -127,23 +120,12 @@
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
             val selectedDots by collectLastValue(underTest.selectedDots)
             val currentDot by collectLastValue(underTest.currentDot)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pattern
-            )
-            utils.authenticationRepository.setUnlocked(false)
-            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+            transitionToPatternBouncer()
             underTest.onShown()
             underTest.onDragStart()
             assertThat(currentDot).isNull()
             CORRECT_PATTERN.forEachIndexed { index, coordinate ->
-                underTest.onDrag(
-                    xPx = 30f * coordinate.x + 15,
-                    yPx = 30f * coordinate.y + 15,
-                    containerSizePx = 90,
-                    verticalOffsetPx = 0f,
-                )
+                dragToCoordinate(coordinate)
                 assertWithMessage("Wrong selected dots for index $index")
                     .that(selectedDots)
                     .isEqualTo(
@@ -176,23 +158,10 @@
             val message by collectLastValue(bouncerViewModel.message)
             val selectedDots by collectLastValue(underTest.selectedDots)
             val currentDot by collectLastValue(underTest.currentDot)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pattern
-            )
-            utils.authenticationRepository.setUnlocked(false)
-            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+            transitionToPatternBouncer()
             underTest.onShown()
             underTest.onDragStart()
-            CORRECT_PATTERN.subList(0, 3).forEach { coordinate ->
-                underTest.onDrag(
-                    xPx = 30f * coordinate.x + 15,
-                    yPx = 30f * coordinate.y + 15,
-                    containerSizePx = 90,
-                    verticalOffsetPx = 0f,
-                )
-            }
+            CORRECT_PATTERN.subList(0, 3).forEach { coordinate -> dragToCoordinate(coordinate) }
 
             underTest.onDragEnd()
 
@@ -203,6 +172,147 @@
         }
 
     @Test
+    fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheSameRow() =
+        testScope.runTest {
+            val selectedDots by collectLastValue(underTest.selectedDots)
+            transitionToPatternBouncer()
+            underTest.onShown()
+
+            /*
+             * Pattern setup, coordinates are (column, row)
+             *   0  1  2
+             * 0 x  x  x
+             * 1 x  x  x
+             * 2 x  x  x
+             */
+            // Select (0,0), Skip over (1,0) and select (2,0)
+            dragOverCoordinates(Point(0, 0), Point(2, 0))
+
+            assertThat(selectedDots)
+                .isEqualTo(
+                    listOf(
+                        PatternDotViewModel(0, 0),
+                        PatternDotViewModel(1, 0),
+                        PatternDotViewModel(2, 0)
+                    )
+                )
+        }
+
+    @Test
+    fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheSameColumn() =
+        testScope.runTest {
+            val selectedDots by collectLastValue(underTest.selectedDots)
+            transitionToPatternBouncer()
+            underTest.onShown()
+
+            /*
+             * Pattern setup, coordinates are (column, row)
+             *   0  1  2
+             * 0 x  x  x
+             * 1 x  x  x
+             * 2 x  x  x
+             */
+            // Select (1,0), Skip over (1,1) and select (1, 2)
+            dragOverCoordinates(Point(1, 0), Point(1, 2))
+
+            assertThat(selectedDots)
+                .isEqualTo(
+                    listOf(
+                        PatternDotViewModel(1, 0),
+                        PatternDotViewModel(1, 1),
+                        PatternDotViewModel(1, 2)
+                    )
+                )
+        }
+
+    @Test
+    fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheDiagonal() =
+        testScope.runTest {
+            val selectedDots by collectLastValue(underTest.selectedDots)
+            transitionToPatternBouncer()
+            underTest.onShown()
+
+            /*
+             * Pattern setup
+             *   0  1  2
+             * 0 x  x  x
+             * 1 x  x  x
+             * 2 x  x  x
+             *
+             * Coordinates are (column, row)
+             * Select (2,0), Skip over (1,1) and select (0, 2)
+             */
+            dragOverCoordinates(Point(2, 0), Point(0, 2))
+
+            assertThat(selectedDots)
+                .isEqualTo(
+                    listOf(
+                        PatternDotViewModel(2, 0),
+                        PatternDotViewModel(1, 1),
+                        PatternDotViewModel(0, 2)
+                    )
+                )
+        }
+
+    @Test
+    fun onDrag_shouldNotIncludeDotIfItIsNotOnTheLine() =
+        testScope.runTest {
+            val selectedDots by collectLastValue(underTest.selectedDots)
+            transitionToPatternBouncer()
+            underTest.onShown()
+
+            /*
+             * Pattern setup
+             *   0  1  2
+             * 0 x  x  x
+             * 1 x  x  x
+             * 2 x  x  x
+             *
+             * Coordinates are (column, row)
+             */
+            dragOverCoordinates(Point(0, 0), Point(1, 0), Point(2, 0), Point(0, 1))
+
+            assertThat(selectedDots)
+                .isEqualTo(
+                    listOf(
+                        PatternDotViewModel(0, 0),
+                        PatternDotViewModel(1, 0),
+                        PatternDotViewModel(2, 0),
+                        PatternDotViewModel(0, 1),
+                    )
+                )
+        }
+
+    @Test
+    fun onDrag_shouldNotIncludeSkippedOverDotsIfTheyAreAlreadySelected() =
+        testScope.runTest {
+            val selectedDots by collectLastValue(underTest.selectedDots)
+            transitionToPatternBouncer()
+            underTest.onShown()
+
+            /*
+             * Pattern setup
+             *   0  1  2
+             * 0 x  x  x
+             * 1 x  x  x
+             * 2 x  x  x
+             *
+             * Coordinates are (column, row)
+             */
+            dragOverCoordinates(Point(1, 0), Point(1, 1), Point(0, 0), Point(2, 0))
+
+            assertThat(selectedDots)
+                .isEqualTo(
+                    listOf(
+                        PatternDotViewModel(1, 0),
+                        PatternDotViewModel(1, 1),
+                        PatternDotViewModel(0, 0),
+                        PatternDotViewModel(2, 0),
+                    )
+                )
+        }
+
+    @Test
     fun onDragEnd_whenPatternTooShort() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
@@ -252,23 +362,10 @@
             val message by collectLastValue(bouncerViewModel.message)
             val selectedDots by collectLastValue(underTest.selectedDots)
             val currentDot by collectLastValue(underTest.currentDot)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pattern
-            )
-            utils.authenticationRepository.setUnlocked(false)
-            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+            transitionToPatternBouncer()
             underTest.onShown()
             underTest.onDragStart()
-            CORRECT_PATTERN.subList(2, 7).forEach { coordinate ->
-                underTest.onDrag(
-                    xPx = 30f * coordinate.x + 15,
-                    yPx = 30f * coordinate.y + 15,
-                    containerSizePx = 90,
-                    verticalOffsetPx = 0f,
-                )
-            }
+            CORRECT_PATTERN.subList(2, 7).forEach { coordinate -> dragToCoordinate(coordinate) }
             underTest.onDragEnd()
             assertThat(selectedDots).isEmpty()
             assertThat(currentDot).isNull()
@@ -276,20 +373,36 @@
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
             // Enter the correct pattern:
-            CORRECT_PATTERN.forEach { coordinate ->
-                underTest.onDrag(
-                    xPx = 30f * coordinate.x + 15,
-                    yPx = 30f * coordinate.y + 15,
-                    containerSizePx = 90,
-                    verticalOffsetPx = 0f,
-                )
-            }
+            CORRECT_PATTERN.forEach { coordinate -> dragToCoordinate(coordinate) }
 
             underTest.onDragEnd()
 
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
         }
 
+    private fun dragOverCoordinates(vararg coordinatesDragged: Point) {
+        underTest.onDragStart()
+        coordinatesDragged.forEach { dragToCoordinate(it) }
+    }
+
+    private fun dragToCoordinate(coordinate: Point) {
+        underTest.onDrag(
+            xPx = dotSize * coordinate.x + 15f,
+            yPx = dotSize * coordinate.y + 15f,
+            containerSizePx = containerSize,
+            verticalOffsetPx = 0f,
+        )
+    }
+
+    private fun TestScope.transitionToPatternBouncer() {
+        utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern)
+        utils.authenticationRepository.setUnlocked(false)
+        sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
+        sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+        assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
+            .isEqualTo(SceneModel(SceneKey.Bouncer))
+    }
+
     companion object {
         private const val ENTER_YOUR_PATTERN = "Enter your pattern"
         private const val WRONG_PATTERN = "Wrong pattern"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index d28f530..ddf788e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.shared.CommunalAppWidgetInfo
 import com.android.systemui.coroutines.collectLastValue
@@ -44,6 +45,7 @@
 
     private lateinit var testScope: TestScope
 
+    private lateinit var communalRepository: FakeCommunalRepository
     private lateinit var widgetRepository: FakeCommunalWidgetRepository
     private lateinit var interactor: CommunalInteractor
 
@@ -52,12 +54,13 @@
         MockitoAnnotations.initMocks(this)
 
         testScope = TestScope()
+        communalRepository = FakeCommunalRepository()
         widgetRepository = FakeCommunalWidgetRepository()
-        interactor = CommunalInteractor(widgetRepository)
+        interactor = CommunalInteractor(communalRepository, widgetRepository)
     }
 
     @Test
-    fun testAppWidgetInfoFlow() =
+    fun appWidgetInfoFlow() =
         testScope.runTest {
             val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
             runCurrent()
@@ -67,4 +70,22 @@
             runCurrent()
             assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
         }
+
+    @Test
+    fun communalEnabled() =
+        testScope.runTest {
+            communalRepository.setIsCommunalEnabled(true)
+
+            val interactor = CommunalInteractor(communalRepository, widgetRepository)
+            assertThat(interactor.isCommunalEnabled).isTrue()
+        }
+
+    @Test
+    fun communalDisabled() =
+        testScope.runTest {
+            communalRepository.setIsCommunalEnabled(false)
+
+            val interactor = CommunalInteractor(communalRepository, widgetRepository)
+            assertThat(interactor.isCommunalEnabled).isFalse()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 3a0883b..511562f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
@@ -62,6 +63,7 @@
     @Before
     fun setup() {
         setDisplays(emptyList())
+        setAllDisplaysIncludingDisabled()
         displayRepository =
             DisplayRepositoryImpl(
                 displayManager,
@@ -70,6 +72,7 @@
                 UnconfinedTestDispatcher()
             )
         verify(displayManager, never()).registerDisplayListener(any(), any())
+        verify(displayManager, never()).getDisplays(any())
     }
 
     @Test
@@ -351,6 +354,22 @@
         }
 
     @Test
+    fun initialState_onePendingDisplayOnBoot_notNull() =
+        testScope.runTest {
+            // 1 is not enabled, but just connected. It should be seen as pending
+            setAllDisplaysIncludingDisabled(0, 1)
+            setDisplays(0) // 0 is enabled.
+            verify(displayManager, never()).getDisplays(any())
+
+            val pendingDisplay by collectLastValue(displayRepository.pendingDisplay)
+
+            verify(displayManager).getDisplays(any())
+
+            assertThat(pendingDisplay).isNotNull()
+            assertThat(pendingDisplay!!.id).isEqualTo(1)
+        }
+
+    @Test
     fun onPendingDisplay_internalDisplay_ignored() =
         testScope.runTest {
             val pendingDisplay by lastPendingDisplay()
@@ -365,7 +384,7 @@
         testScope.runTest {
             val pendingDisplay by lastPendingDisplay()
 
-            sendOnDisplayConnected(1, Display.TYPE_EXTERNAL)
+            sendOnDisplayConnected(1, TYPE_EXTERNAL)
             sendOnDisplayConnected(2, Display.TYPE_INTERNAL)
 
             assertThat(pendingDisplay!!.id).isEqualTo(1)
@@ -416,7 +435,7 @@
         whenever(displayManager.getDisplay(eq(id))).thenReturn(null)
     }
 
-    private fun sendOnDisplayConnected(id: Int, displayType: Int = Display.TYPE_EXTERNAL) {
+    private fun sendOnDisplayConnected(id: Int, displayType: Int = TYPE_EXTERNAL) {
         val mockDisplay = display(id = id, type = displayType)
         whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay)
         connectedDisplayListener.value.onDisplayConnected(id)
@@ -424,15 +443,25 @@
 
     private fun setDisplays(displays: List<Display>) {
         whenever(displayManager.displays).thenReturn(displays.toTypedArray())
+        displays.forEach { display ->
+            whenever(displayManager.getDisplay(eq(display.displayId))).thenReturn(display)
+        }
+    }
+
+    private fun setAllDisplaysIncludingDisabled(vararg ids: Int) {
+        val displays = ids.map { display(type = TYPE_EXTERNAL, id = it) }.toTypedArray()
+        whenever(
+                displayManager.getDisplays(
+                    eq(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+                )
+            )
+            .thenReturn(displays)
+        displays.forEach { display ->
+            whenever(displayManager.getDisplay(eq(display.displayId))).thenReturn(display)
+        }
     }
 
     private fun setDisplays(vararg ids: Int) {
-        setDisplays(ids.map { display(it) })
-    }
-
-    private fun display(id: Int): Display {
-        return mock<Display>().also { mockDisplay ->
-            whenever(mockDisplay.displayId).thenReturn(id)
-        }
+        setDisplays(ids.map { display(type = TYPE_EXTERNAL, id = it) })
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index f62137c..f3c9432 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -172,7 +172,6 @@
         val featureFlags =
             FakeFeatureFlags().apply {
                 set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
-                set(Flags.REVAMPED_WALLPAPER_UI, true)
                 set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
                 set(Flags.FACE_AUTH_REFACTOR, true)
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index f78d051..9d96ac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -186,10 +186,10 @@
     private @Mock AuthController mAuthController;
     private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
     private @Mock ShadeWindowLogger mShadeWindowLogger;
-    private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
-            mKeyguardUpdateMonitorCallbackCaptor;
     private @Captor ArgumentCaptor<KeyguardStateController.Callback>
             mKeyguardStateControllerCallback;
+    private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
+            mKeyguardUpdateMonitorCallbackCaptor;
     private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -313,6 +313,26 @@
     }
 
     @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally() {
+        // GIVEN keyguard is enabled and lockdown occurred so the keyguard is showing
+        mViewMediator.onSystemReady();
+        mViewMediator.setKeyguardEnabled(true);
+        TestableLooper.get(this).processAllMessages();
+        captureKeyguardUpdateMonitorCallback();
+        when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true);
+        mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0);
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+
+        // WHEN keyguard is externally not enabled anymore
+        mViewMediator.setKeyguardEnabled(false);
+
+        // THEN keyguard is NOT dismissed; it continues to show
+        TestableLooper.get(this).processAllMessages();
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+    }
+
+    @Test
     public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
         verify(mUpdateMonitor).dispatchKeyguardGoingAway(false);
@@ -1120,11 +1140,11 @@
         mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
     }
 
-    private void captureKeyguardUpdateMonitorCallback() {
-        verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture());
-    }
-
     private void captureKeyguardStateControllerCallback() {
         verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture());
     }
+
+    private void captureKeyguardUpdateMonitorCallback() {
+        verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index d36e778..b9c0b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -20,6 +20,7 @@
 import android.graphics.drawable.Drawable
 import android.service.quickaccesswallet.GetWalletCardsResponse
 import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.service.quickaccesswallet.WalletCard
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -38,6 +39,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runBlockingTest
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -92,6 +94,40 @@
     }
 
     @Test
+    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
+        runTest(UnconfinedTestDispatcher()) {
+            setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
+            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+            job.cancel()
+        }
+
+    @Test
+    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
+        runTest(UnconfinedTestDispatcher()) {
+            setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
+            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+            val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+            assertThat(visibleModel.icon)
+                .isEqualTo(
+                    Icon.Loaded(
+                        drawable = ICON,
+                        contentDescription =
+                            ContentDescription.Resource(
+                                res = R.string.accessibility_wallet_button,
+                            ),
+                    )
+                )
+            job.cancel()
+        }
+
+    @Test
     fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
         setUpState(isWalletFeatureAvailable = false)
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -187,6 +223,7 @@
         isWalletServiceAvailable: Boolean = true,
         isWalletQuerySuccessful: Boolean = true,
         hasSelectedCard: Boolean = true,
+        cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
     ) {
         val walletClient: QuickAccessWalletClient = mock()
         whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -202,7 +239,19 @@
                 if (isWalletQuerySuccessful) {
                     onWalletCardsRetrieved(
                         if (hasSelectedCard) {
-                            GetWalletCardsResponse(listOf(mock()), 0)
+                            GetWalletCardsResponse(
+                                listOf(
+                                    WalletCard.Builder(
+                                            /*cardId= */ CARD_ID,
+                                            /*cardType= */ cardType,
+                                            /*cardImage= */ mock(),
+                                            /*contentDescription=  */ CARD_DESCRIPTION,
+                                            /*pendingIntent= */ mock()
+                                        )
+                                        .build()
+                                ),
+                                0
+                            )
                         } else {
                             GetWalletCardsResponse(emptyList(), 0)
                         }
@@ -216,5 +265,7 @@
 
     companion object {
         private val ICON: Drawable = mock()
+        private const val CARD_ID: String = "Id"
+        private const val CARD_DESCRIPTION: String = "Description"
     }
 }
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 f234582..6b194f2 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
@@ -36,13 +36,14 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
 import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -208,7 +209,7 @@
                 applicationScope = testScope.backgroundScope,
                 context = context,
                 mainExecutor = FakeExecutor(FakeSystemClock()),
-                rearDisplayStateRepository = FakeRearDisplayStateRepository(),
+                displayStateRepository = FakeDisplayStateRepository(),
                 displayRepository = displayRepository,
             )
 
@@ -285,7 +286,7 @@
             allPreconditionsToRunFaceAuthAreTrue()
 
             FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
             uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
 
@@ -318,12 +319,12 @@
             initCollectors()
             allPreconditionsToRunFaceAuthAreTrue()
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
             clearInvocations(faceManager)
             clearInvocations(uiEventLogger)
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             verifyNoMoreInteractions(faceManager)
             verifyNoMoreInteractions(uiEventLogger)
         }
@@ -335,7 +336,7 @@
             verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
             allPreconditionsToRunFaceAuthAreTrue()
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
 
             authenticationCallback.value.onAuthenticationError(
@@ -389,7 +390,7 @@
             initCollectors()
             allPreconditionsToRunFaceAuthAreTrue()
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
 
             var wasAuthCancelled = false
@@ -443,7 +444,7 @@
             initCollectors()
             allPreconditionsToRunFaceAuthAreTrue()
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
 
             // Enter cancelling state
@@ -451,7 +452,7 @@
             clearInvocations(faceManager)
 
             // Auth is while cancelling.
-            underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
             // Auth is not started
             verifyNoMoreInteractions(faceManager)
 
@@ -474,14 +475,14 @@
             initCollectors()
             allPreconditionsToRunFaceAuthAreTrue()
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
 
             clearInvocations(faceManager)
             underTest.cancel()
             advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
         }
 
@@ -492,7 +493,7 @@
             allPreconditionsToRunFaceAuthAreTrue()
             val emittedValues by collectValues(underTest.authenticationStatus)
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             underTest.cancel()
             advanceTimeBy(100)
             underTest.cancel()
@@ -519,7 +520,7 @@
             initCollectors()
             allPreconditionsToRunFaceAuthAreTrue()
 
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
 
             authenticationCallback.value.onAuthenticationHelp(9, "help msg")
@@ -562,8 +563,26 @@
         }
 
     @Test
-    fun authenticateDoesNotRunIfFaceAuthIsCurrentlyPaused() =
-        testScope.runTest { testGatingCheckForFaceAuth { underTest.pauseFaceAuth() } }
+    fun authenticateDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                fakeUserRepository.setSelectedUserInfo(
+                    primaryUser,
+                    SelectionStatus.SELECTION_IN_PROGRESS
+                )
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunIfUserSwitchingIsCurrentlyInProgress() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                fakeUserRepository.setSelectedUserInfo(
+                    userInfo = primaryUser,
+                    selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+                )
+            }
+        }
 
     @Test
     fun authenticateDoesNotRunIfKeyguardIsNotShowing() =
@@ -582,12 +601,6 @@
         testScope.runTest { testGatingCheckForFaceAuth { underTest.setLockedOut(true) } }
 
     @Test
-    fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
-        testScope.runTest {
-            testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
-        }
-
-    @Test
     fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
         testScope.runTest {
             testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
@@ -608,14 +621,6 @@
         }
 
     @Test
-    fun authenticateDoesNotRunWhenFaceAuthIsNotCurrentlyAllowedToRun() =
-        testScope.runTest {
-            testGatingCheckForFaceAuth {
-                biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
-            }
-        }
-
-    @Test
     fun authenticateDoesNotRunWhenSecureCameraIsActive() =
         testScope.runTest {
             testGatingCheckForFaceAuth {
@@ -672,7 +677,7 @@
             // Flip one precondition to false.
             biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
             assertThat(canFaceAuthRun()).isFalse()
-            underTest.authenticate(
+            underTest.requestAuthenticate(
                 FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
                 fallbackToDetection = true
             )
@@ -693,7 +698,7 @@
 
             trustRepository.setCurrentUserTrusted(true)
             assertThat(canFaceAuthRun()).isFalse()
-            underTest.authenticate(
+            underTest.requestAuthenticate(
                 FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
                 fallbackToDetection = true
             )
@@ -884,10 +889,6 @@
         }
 
     @Test
-    fun detectDoesNotRunWhenUserSwitchingInProgress() =
-        testScope.runTest { testGatingCheckForDetect { underTest.pauseFaceAuth() } }
-
-    @Test
     fun detectDoesNotRunWhenKeyguardGoingAway() =
         testScope.runTest {
             testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
@@ -1075,6 +1076,28 @@
             faceAuthenticateIsCalled()
         }
 
+    @Test
+    fun queuedAuthOnlyRequestShouldNotBeProcessedIfOnlyDetectionCanBeRun() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            // This will prevent auth from running but not detection
+            biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
+
+            runCurrent()
+            assertThat(canFaceAuthRun()).isFalse()
+
+            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
+            runCurrent()
+
+            faceDetectIsNotCalled()
+            faceAuthenticateIsNotCalled()
+
+            biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+            faceAuthenticateIsCalled()
+        }
+
     private suspend fun TestScope.testGatingCheckForFaceAuth(
         gatingCheckModifier: suspend () -> Unit
     ) {
@@ -1087,10 +1110,18 @@
         // gating check doesn't allow face auth to run.
         assertThat(underTest.canRunFaceAuth.value).isFalse()
 
+        // request face auth just before gating conditions become true, this ensures any race
+        // conditions won't prevent face auth from running
+        underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
+        faceAuthenticateIsNotCalled()
+
         // flip the gating check back on.
         allPreconditionsToRunFaceAuthAreTrue()
+        assertThat(underTest.canRunFaceAuth.value).isTrue()
 
-        triggerFaceAuth(false)
+        faceAuthenticateIsCalled()
+        assertThat(authRunning()).isTrue()
+        cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
 
         // Flip gating check off
         gatingCheckModifier()
@@ -1101,13 +1132,17 @@
         clearInvocations(faceManager)
 
         // Try auth again
-        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+        underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+        runCurrent()
 
         // Auth can't run again
         faceAuthenticateIsNotCalled()
     }
 
-    private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+    private suspend fun TestScope.testGatingCheckForDetect(
+        gatingCheckModifier: suspend () -> Unit
+    ) {
         initCollectors()
         allPreconditionsToRunFaceAuthAreTrue()
 
@@ -1118,7 +1153,11 @@
         assertThat(canFaceAuthRun()).isFalse()
 
         // Trigger authenticate with detection fallback
-        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+        underTest.requestAuthenticate(
+            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+            fallbackToDetection = true
+        )
+        runCurrent()
 
         faceAuthenticateIsNotCalled()
         faceDetectIsCalled()
@@ -1133,15 +1172,21 @@
         clearInvocations(faceManager)
 
         // Try to run detect again
-        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+        underTest.requestAuthenticate(
+            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+            fallbackToDetection = true
+        )
 
         // Detect won't run because preconditions are not true anymore.
         faceDetectIsNotCalled()
     }
 
-    private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+    private fun TestScope.triggerFaceAuth(fallbackToDetect: Boolean) {
         assertThat(canFaceAuthRun()).isTrue()
-        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+        underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+
+        runCurrent()
+
         faceAuthenticateIsCalled()
         assertThat(authRunning()).isTrue()
         cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
@@ -1150,7 +1195,6 @@
     private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
         verify(faceManager, atLeastOnce())
             .addLockoutResetCallback(faceLockoutResetCallback.capture())
-        underTest.resumeFaceAuth()
         trustRepository.setCurrentUserTrusted(false)
         keyguardRepository.setKeyguardGoingAway(false)
         keyguardRepository.setWakefulnessModel(
@@ -1164,7 +1208,7 @@
         biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
         biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
         biometricSettingsRepository.setIsUserInLockdown(false)
-        fakeUserRepository.setSelectedUserInfo(primaryUser)
+        fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
         faceLockoutResetCallback.value.onLockoutReset(0)
         bouncerRepository.setAlternateVisible(true)
         keyguardRepository.setKeyguardShowing(true)
@@ -1187,7 +1231,9 @@
 
     private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
 
-    private fun faceDetectIsCalled() {
+    private fun TestScope.faceDetectIsCalled() {
+        runCurrent()
+
         verify(faceManager)
             .detectFace(
                 cancellationSignal.capture(),
@@ -1196,7 +1242,9 @@
             )
     }
 
-    private fun faceAuthenticateIsCalled() {
+    private fun TestScope.faceAuthenticateIsCalled() {
+        runCurrent()
+
         verify(faceManager)
             .authenticate(
                 isNull(),
@@ -1207,7 +1255,9 @@
             )
     }
 
-    private fun faceAuthenticateIsNotCalled() {
+    private fun TestScope.faceAuthenticateIsNotCalled() {
+        runCurrent()
+
         verify(faceManager, never())
             .authenticate(
                 isNull(),
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 da70a9f..2ed9de2 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
@@ -224,23 +224,7 @@
         }
 
     @Test
-    fun faceAuthIsPausedWhenUserSwitchingIsInProgress() =
-        testScope.runTest {
-            underTest.start()
-
-            fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
-            runCurrent()
-            fakeUserRepository.setSelectedUserInfo(
-                secondaryUser,
-                SelectionStatus.SELECTION_IN_PROGRESS
-            )
-            runCurrent()
-
-            assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
-        }
-
-    @Test
-    fun faceAuthIsUnpausedWhenUserSwitchingIsInComplete() =
+    fun faceAuthLockedOutStateIsUpdatedAfterUserSwitch() =
         testScope.runTest {
             underTest.start()
 
@@ -251,7 +235,6 @@
                 SelectionStatus.SELECTION_IN_PROGRESS
             )
             runCurrent()
-            assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
 
             bouncerRepository.setPrimaryShow(true)
             // New user is not locked out.
@@ -262,7 +245,6 @@
             )
             runCurrent()
 
-            assertThat(faceAuthRepository.isFaceAuthPaused()).isFalse()
             assertThat(faceAuthRepository.isLockedOut.value).isFalse()
 
             runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 14cdf2f..90e217f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -217,6 +217,7 @@
                     fromScene = SceneKey.Gone,
                     toScene = SceneKey.Lockscreen,
                     progress = flowOf(0f),
+                    isUserInputDriven = false,
                 )
             runCurrent()
             assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 0050d64..0bbeeff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -302,7 +302,6 @@
                 featureFlags =
                     FakeFeatureFlags().apply {
                         set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
-                        set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled)
                         set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, isOpenWppDirectlyEnabled)
                     },
                 broadcastDispatcher = fakeBroadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 681fce8..7f4755d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -23,8 +23,6 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -35,6 +33,7 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
 import com.android.systemui.util.mockito.whenever
@@ -60,12 +59,11 @@
     private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
     @Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
     @Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
+    @Mock private lateinit var defaultStatusBarViewSection: DefaultStatusBarSection
     @Mock private lateinit var defaultNSSLSection: DefaultNotificationStackScrollLayoutSection
     @Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
     @Mock private lateinit var aodNotificationIconsSection: AodNotificationIconsSection
 
-    private val featureFlags = FakeFeatureFlags()
-
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -78,26 +76,17 @@
                 defaultAmbientIndicationAreaSection,
                 defaultSettingsPopupMenuSection,
                 defaultStatusViewSection,
+                defaultStatusBarViewSection,
                 defaultNSSLSection,
                 splitShadeGuidelines,
                 aodNotificationIconsSection,
-                featureFlags,
             )
-        featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, false)
     }
 
     @Test
     fun replaceViews() {
         val constraintLayout = ConstraintLayout(context, null)
         underTest.replaceViews(null, constraintLayout)
-        underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) }
-    }
-
-    @Test
-    fun replaceViews_lazyInflateFlagOn() {
-        featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
-        val constraintLayout = ConstraintLayout(context, null)
-        underTest.replaceViews(null, constraintLayout)
         underTest.sections.forEach { verify(it).addViews(constraintLayout) }
     }
 
@@ -107,7 +96,6 @@
         val someSection = mock(KeyguardSection::class.java)
         whenever(prevBlueprint.sections)
             .thenReturn(underTest.sections.minus(defaultLockIconSection).plus(someSection))
-        featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.replaceViews(prevBlueprint, constraintLayout)
         underTest.sections.minus(defaultLockIconSection).forEach {
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 b30dc9c..1681cfd 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
@@ -42,14 +42,7 @@
             repository = utils.authenticationRepository(),
         )
 
-    private val underTest =
-        LockscreenSceneViewModel(
-            authenticationInteractor = authenticationInteractor,
-            longPress =
-                KeyguardLongPressViewModel(
-                    interactor = mock(),
-                ),
-        )
+    private val underTest = createLockscreenSceneViewModel()
 
     @Test
     fun upTransitionSceneKey_canSwipeToUnlock_gone() =
@@ -73,4 +66,34 @@
 
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
         }
+
+    @Test
+    fun leftTransitionSceneKey_communalIsEnabled_communal() =
+        testScope.runTest {
+            utils.communalRepository.setIsCommunalEnabled(true)
+            val underTest = createLockscreenSceneViewModel()
+
+            assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+        }
+
+    @Test
+    fun leftTransitionSceneKey_communalIsDisabled_null() =
+        testScope.runTest {
+            utils.communalRepository.setIsCommunalEnabled(false)
+            val underTest = createLockscreenSceneViewModel()
+
+            assertThat(underTest.leftDestinationSceneKey).isNull()
+        }
+
+    private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
+        return LockscreenSceneViewModel(
+            applicationScope = testScope.backgroundScope,
+            authenticationInteractor = authenticationInteractor,
+            communalInteractor = utils.communalInteractor(),
+            longPress =
+                KeyguardLongPressViewModel(
+                    interactor = mock(),
+                ),
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index d1299d4..5939bb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.media.controls.pipeline
 
+import android.app.IUriGrantsManager
 import android.app.Notification
 import android.app.Notification.FLAG_NO_CLEAR
 import android.app.Notification.MediaStyle
 import android.app.PendingIntent
+import android.app.UriGrantsManager
 import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceConfig
 import android.app.smartspace.SmartspaceManager
@@ -27,12 +29,14 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
+import android.graphics.ImageDecoder
 import android.graphics.drawable.Icon
 import android.media.MediaDescription
 import android.media.MediaMetadata
 import android.media.session.MediaController
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
+import android.net.Uri
 import android.os.Bundle
 import android.provider.Settings
 import android.service.notification.StatusBarNotification
@@ -40,6 +44,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.media.utils.MediaConstants
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.InstanceIdSequenceFake
@@ -83,7 +88,9 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoSession
 import org.mockito.junit.MockitoJUnit
+import org.mockito.quality.Strictness
 
 private const val KEY = "KEY"
 private const val KEY_2 = "KEY_2"
@@ -149,6 +156,8 @@
     @Captor lateinit var stateCallbackCaptor: ArgumentCaptor<(String, PlaybackState) -> Unit>
     @Captor lateinit var sessionCallbackCaptor: ArgumentCaptor<(String) -> Unit>
     @Captor lateinit var smartSpaceConfigBuilderCaptor: ArgumentCaptor<SmartspaceConfig>
+    @Mock private lateinit var ugm: IUriGrantsManager
+    @Mock private lateinit var imageSource: ImageDecoder.Source
 
     private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
 
@@ -159,8 +168,17 @@
             1
         )
 
+    private lateinit var staticMockSession: MockitoSession
+
     @Before
     fun setup() {
+        staticMockSession =
+            ExtendedMockito.mockitoSession()
+                .mockStatic<UriGrantsManager>(UriGrantsManager::class.java)
+                .mockStatic<ImageDecoder>(ImageDecoder::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        whenever(UriGrantsManager.getService()).thenReturn(ugm)
         foregroundExecutor = FakeExecutor(clock)
         backgroundExecutor = FakeExecutor(clock)
         uiExecutor = FakeExecutor(clock)
@@ -270,6 +288,7 @@
 
     @After
     fun tearDown() {
+        staticMockSession.finishMocking()
         session.release()
         mediaDataManager.destroy()
         Settings.Secure.putInt(
@@ -2198,6 +2217,66 @@
         verify(listener).onMediaDataRemoved(eq(KEY))
     }
 
+    @Test
+    fun testResumeMediaLoaded_hasArtPermission_artLoaded() {
+        // When resume media is loaded and user/app has permission to access the art URI,
+        whenever(
+                ugm.checkGrantUriPermission_ignoreNonSystem(
+                    anyInt(),
+                    any(),
+                    any(),
+                    anyInt(),
+                    anyInt()
+                )
+            )
+            .thenReturn(1)
+        val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+        val uri = Uri.parse("content://example")
+        whenever(ImageDecoder.createSource(any(), eq(uri))).thenReturn(imageSource)
+        whenever(ImageDecoder.decodeBitmap(any(), any())).thenReturn(artwork)
+
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                setIconUri(uri)
+                build()
+            }
+        addResumeControlAndLoad(desc)
+
+        // Then the artwork is loaded
+        assertThat(mediaDataCaptor.value.artwork).isNotNull()
+    }
+
+    @Test
+    fun testResumeMediaLoaded_noArtPermission_noArtLoaded() {
+        // When resume media is loaded and user/app does not have permission to access the art URI
+        whenever(
+                ugm.checkGrantUriPermission_ignoreNonSystem(
+                    anyInt(),
+                    any(),
+                    any(),
+                    anyInt(),
+                    anyInt()
+                )
+            )
+            .thenThrow(SecurityException("Test no permission"))
+        val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+        val uri = Uri.parse("content://example")
+        whenever(ImageDecoder.createSource(any(), eq(uri))).thenReturn(imageSource)
+        whenever(ImageDecoder.decodeBitmap(any(), any())).thenReturn(artwork)
+
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                setIconUri(uri)
+                build()
+            }
+        addResumeControlAndLoad(desc)
+
+        // Then the artwork is not loaded
+        assertThat(mediaDataCaptor.value.artwork).isNull()
+    }
+
     /** Helper function to add a basic media notification and capture the resulting MediaData */
     private fun addNotificationAndLoad() {
         addNotificationAndLoad(mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index 91b0245..7ad2ce8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
@@ -90,6 +91,7 @@
                 settings,
                 fakeHandler,
                 configurationController,
+                ResourcesSplitShadeStateController()
             )
         keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
         keyguardMediaController.useSplitShade = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 2ce236d..33ed01f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
@@ -120,6 +121,7 @@
                 notifPanelEvents,
                 settings,
                 fakeHandler,
+                ResourcesSplitShadeStateController()
             )
         verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
         verify(statusBarStateController).addCallback(statusBarCallback.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 89405c1..c835146 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -74,11 +74,11 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
-public class NavigationBarControllerTest extends SysuiTestCase {
+public class NavigationBarControllerImplTest extends SysuiTestCase {
 
     private static final int SECONDARY_DISPLAY = 1;
 
-    private NavigationBarController mNavigationBarController;
+    private NavigationBarControllerImpl mNavigationBarController;
     private NavigationBar mDefaultNavBar;
     private NavigationBar mSecondaryNavBar;
     private StaticMockitoSession mMockitoSession;
@@ -95,7 +95,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mNavigationBarController = spy(
-                new NavigationBarController(mContext,
+                new NavigationBarControllerImpl(mContext,
                         mock(OverviewProxyService.class),
                         mock(NavigationModeController.class),
                         mock(SysUiState.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
index 7369c82..52d02b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
@@ -33,8 +33,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
-import com.android.systemui.navigationbar.NavigationBarInflaterView;
-import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.recents.OverviewProxyService;
 
 import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 1536c17..b50032f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -73,6 +73,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -705,12 +706,12 @@
     fun updateNoteTaskAsUser_sameUser_shouldUpdateShortcuts() {
         val user = UserHandle.CURRENT
         val controller = spy(createNoteTaskController())
-        doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+        doNothing().whenever(controller).launchUpdateNoteTaskAsUser(any())
         whenever(controller.getCurrentRunningUser()).thenReturn(user)
 
         controller.updateNoteTaskAsUser(user)
 
-        verify(controller).updateNoteTaskAsUserInternal(user)
+        verify(controller).launchUpdateNoteTaskAsUser(user)
         verify(context, never()).startServiceAsUser(any(), any())
     }
 
@@ -718,12 +719,12 @@
     fun updateNoteTaskAsUser_differentUser_shouldUpdateShortcutsInUserProcess() {
         val user = UserHandle.CURRENT
         val controller = spy(createNoteTaskController(isEnabled = true))
-        doNothing().whenever(controller).updateNoteTaskAsUserInternal(any())
+        doNothing().whenever(controller).launchUpdateNoteTaskAsUser(any())
         whenever(controller.getCurrentRunningUser()).thenReturn(UserHandle.SYSTEM)
 
         controller.updateNoteTaskAsUser(user)
 
-        verify(controller, never()).updateNoteTaskAsUserInternal(any())
+        verify(controller, never()).launchUpdateNoteTaskAsUser(any())
         val intent = withArgCaptor { verify(context).startServiceAsUser(capture(), eq(user)) }
         assertThat(intent).hasComponentClass(NoteTaskControllerUpdateService::class.java)
     }
@@ -733,7 +734,8 @@
     @Test
     fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
         createNoteTaskController(isEnabled = true)
-            .updateNoteTaskAsUserInternal(userTracker.userHandle)
+            .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+        testScope.runCurrent()
 
         val actualComponent = argumentCaptor<ComponentName>()
         verify(context.packageManager)
@@ -768,7 +770,8 @@
             .thenReturn(emptyList())
 
         createNoteTaskController(isEnabled = true)
-            .updateNoteTaskAsUserInternal(userTracker.userHandle)
+            .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+        testScope.runCurrent()
 
         val argument = argumentCaptor<ComponentName>()
         verify(context.packageManager)
@@ -787,7 +790,8 @@
     @Test
     fun updateNoteTaskAsUserInternal_flagDisabled_shouldDisableShortcuts() {
         createNoteTaskController(isEnabled = false)
-            .updateNoteTaskAsUserInternal(userTracker.userHandle)
+            .launchUpdateNoteTaskAsUser(userTracker.userHandle)
+        testScope.runCurrent()
 
         val argument = argumentCaptor<ComponentName>()
         verify(context.packageManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 95bb3e0..7833007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -22,6 +22,8 @@
 import android.testing.AndroidTestingRunner
 import android.view.KeyEvent
 import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import android.view.KeyEvent.KEYCODE_N
 import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +32,6 @@
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -43,7 +44,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
@@ -66,6 +66,7 @@
 
     private val executor = FakeExecutor(FakeSystemClock())
     private val userTracker = FakeUserTracker()
+    private val handlerCallbacks = mutableListOf<Runnable>()
 
     @Before
     fun setUp() {
@@ -74,19 +75,27 @@
     }
 
     private fun createUnderTest(
-            isEnabled: Boolean,
-            bubbles: Bubbles?,
+        isEnabled: Boolean,
+        bubbles: Bubbles?,
     ): NoteTaskInitializer =
-            NoteTaskInitializer(
-                    controller = controller,
-                    commandQueue = commandQueue,
-                    optionalBubbles = Optional.ofNullable(bubbles),
-                    isEnabled = isEnabled,
-                    roleManager = roleManager,
-                    userTracker = userTracker,
-                    keyguardUpdateMonitor = keyguardMonitor,
-                    backgroundExecutor = executor,
-            )
+        NoteTaskInitializer(
+            controller = controller,
+            commandQueue = commandQueue,
+            optionalBubbles = Optional.ofNullable(bubbles),
+            isEnabled = isEnabled,
+            roleManager = roleManager,
+            userTracker = userTracker,
+            keyguardUpdateMonitor = keyguardMonitor,
+            backgroundExecutor = executor,
+        )
+
+    private fun createKeyEvent(
+        action: Int,
+        code: Int,
+        downTime: Long = 0L,
+        eventTime: Long = 0L,
+        metaState: Int = 0
+    ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
 
     @Test
     fun initialize_withUserUnlocked() {
@@ -120,12 +129,12 @@
         underTest.initialize()
 
         verifyZeroInteractions(
-                commandQueue,
-                bubbles,
-                controller,
-                roleManager,
-                userManager,
-                keyguardMonitor,
+            commandQueue,
+            bubbles,
+            controller,
+            roleManager,
+            userManager,
+            keyguardMonitor,
         )
     }
 
@@ -136,18 +145,23 @@
         underTest.initialize()
 
         verifyZeroInteractions(
-                commandQueue,
-                bubbles,
-                controller,
-                roleManager,
-                userManager,
-                keyguardMonitor,
+            commandQueue,
+            bubbles,
+            controller,
+            roleManager,
+            userManager,
+            keyguardMonitor,
         )
     }
 
     @Test
     fun initialize_handleSystemKey() {
-        val expectedKeyEvent = KeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL)
+        val expectedKeyEvent =
+            createKeyEvent(
+                ACTION_DOWN,
+                KEYCODE_N,
+                metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+            )
         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
         underTest.initialize()
         val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
@@ -176,7 +190,7 @@
         underTest.initialize()
         val callback = withArgCaptor {
             verify(roleManager)
-                    .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
+                .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
         }
 
         callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
@@ -203,4 +217,60 @@
 
         verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
     }
+
+    @Test
+    fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
+        val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+        underTest.initialize()
+        val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+        )
+        verify(controller, never()).showNoteTask(any())
+
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+        )
+
+        verify(controller).showNoteTask(any())
+    }
+
+    @Test
+    fun tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwice() {
+        val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+        underTest.initialize()
+        val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+        )
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+        )
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 99)
+        )
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 150)
+        )
+
+        verify(controller, times(1)).showNoteTask(any())
+    }
+
+    @Test
+    fun tailButtonGestureDetection_longPress_shouldNotShowNoteTask() {
+        val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+        underTest.initialize()
+        val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+        )
+        callback.handleSystemKey(
+            createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 1000)
+        )
+
+        verify(controller, never()).showNoteTask(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java
deleted file mode 100644
index 3e677c0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-
-import static org.junit.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashDrawable;
-import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
-import org.junit.Test;
-
-@SmallTest
-public class AlphaControlledSignalTileViewTest extends SysuiTestCase {
-
-    private AlphaControlledSignalTileView mTileView;
-
-    @Test
-    public void testTileView_createsAlphaControlledSlashImageView() {
-        mTileView = new AlphaControlledSignalTileView(mContext);
-
-        assertTrue(mTileView.createSlashImageView(mContext)
-                instanceof AlphaControlledSlashImageView);
-    }
-
-    /// AlphaControlledSlashImageView tests
-    @Test
-    public void testSlashImageView_createsAlphaControlledSlashDrawable() {
-        TestableSlashImageView iv = new TestableSlashImageView(mContext);
-
-        iv.ensureSlashDrawable();
-        assertTrue(iv.getSlashDrawable() instanceof AlphaControlledSlashDrawable);
-    }
-
-    /// AlphaControlledSlashDrawable tests
-    @Test
-    public void testSlashDrawable_doesNotSetTintList() {
-        Drawable mockDrawable = mock(Drawable.class);
-        AlphaControlledSlashDrawable drawable = new AlphaControlledSlashDrawable(mockDrawable);
-        ColorStateList list = ColorStateList.valueOf(0xffffff);
-        drawable.setTintList(list);
-        verify(mockDrawable, never()).setTintList(any());
-    }
-
-    @Test
-    public void testSlashDrawable_setsFinalTintList() {
-        Drawable mockDrawable = mock(Drawable.class);
-        AlphaControlledSlashDrawable drawable = new AlphaControlledSlashDrawable(mockDrawable);
-        ColorStateList list = ColorStateList.valueOf(0xffffff);
-        drawable.setFinalTintList(list);
-        verify(mockDrawable, atLeastOnce()).setTintList(list);
-    }
-
-    // Expose getSlashDrawable
-    private static class TestableSlashImageView extends AlphaControlledSlashImageView {
-        TestableSlashImageView(Context c) {
-            super(c);
-        }
-
-        private SlashDrawable getSlashDrawable() {
-            return mSlash;
-        }
-
-        @Override
-        protected void setSlash(SlashDrawable slash) {
-            super.setSlash(slash);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index fcda5f5..8afe095 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -66,6 +66,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.util.animation.UniqueObjectHostView;
 
@@ -524,7 +525,10 @@
 
         return new QSFragment(
                 new RemoteInputQuickSettingsDisabler(
-                        context, commandQueue, mock(ConfigurationController.class)),
+                        context,
+                        commandQueue,
+                        new ResourcesSplitShadeStateController(),
+                        mock(ConfigurationController.class)),
                 mStatusBarStateController,
                 commandQueue,
                 mQSMediaHost,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 6720dae..24cd3e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.ContextThemeWrapper;
 
 import androidx.test.filters.SmallTest;
 
@@ -46,10 +47,10 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.ui.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.util.animation.DisappearParameters;
 
 import org.junit.Before;
@@ -91,8 +92,6 @@
     @Mock
     QSTile mOtherTile;
     @Mock
-    QSTileView mQSTileView;
-    @Mock
     PagedTileLayout mPagedTileLayout;
     @Mock
     Resources mResources;
@@ -110,7 +109,7 @@
                 MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
                 DumpManager dumpManager) {
             super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
-                    qsLogger, dumpManager);
+                    qsLogger, dumpManager, new ResourcesSplitShadeStateController());
         }
 
         @Override
@@ -131,11 +130,12 @@
         when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
         when(mQSTile.getTileSpec()).thenReturn("dnd");
         when(mQSHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
-        when(mQSHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
         when(mQSTileRevealControllerFactory.create(any(), any()))
                 .thenReturn(mQSTileRevealController);
         when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
         when(mQSPanel.getResources()).thenReturn(mResources);
+        when(mQSPanel.getContext()).thenReturn(
+                new ContextThemeWrapper(getContext(), R.style.Theme_SystemUI_QuickSettings));
         when(mResources.getConfiguration()).thenReturn(mConfiguration);
         doAnswer(invocation -> {
             when(mQSPanel.isListening()).thenReturn(invocation.getArgument(0));
@@ -208,14 +208,15 @@
 
     @Test
     public void testDump() {
-        String mockTileViewString = "Mock Tile View";
+        String mockTileViewString = "QSTileViewImpl[locInScreen=(0, 0), "
+                + "iconView=QSIconViewImpl[state=-1, tint=0], "
+                + "tileState=false]";
         String mockTileString = "Mock Tile";
         doAnswer(invocation -> {
             PrintWriter pw = invocation.getArgument(0);
             pw.println(mockTileString);
             return null;
         }).when(mQSTile).dump(any(PrintWriter.class), any(String[].class));
-        when(mQSTileView.toString()).thenReturn(mockTileViewString);
 
         StringWriter w = new StringWriter();
         PrintWriter pw = new PrintWriter(w);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 9d9d0c7..4cb19444 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -4,6 +4,7 @@
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableResources
+import android.view.ContextThemeWrapper
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.R
@@ -18,6 +19,7 @@
 import com.android.systemui.settings.brightness.BrightnessController
 import com.android.systemui.settings.brightness.BrightnessSliderController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.tuner.TunerService
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -70,6 +72,8 @@
         whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
         setShouldUseSplitShade(false)
         whenever(qsPanel.resources).thenReturn(testableResources.resources)
+        whenever(qsPanel.context)
+                .thenReturn( ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
         whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout)
         whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
         whenever(qsPanel.setListening(anyBoolean())).then {
@@ -91,7 +95,8 @@
             brightnessControllerFactory,
             brightnessSliderFactory,
             falsingManager,
-            statusBarKeyguardViewManager
+            statusBarKeyguardViewManager,
+                ResourcesSplitShadeStateController()
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index fe6c9b3..1df504a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.plugins.qs.QSTileView
 import com.android.systemui.qs.QSPanelControllerBase.TileRecord
 import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.qs.tileimpl.QSIconViewImpl
 import com.android.systemui.qs.tileimpl.QSTileViewImpl
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -112,7 +111,7 @@
         qsPanel.tileLayout?.addTile(
                 QSPanelControllerBase.TileRecord(
                     mock(QSTile::class.java),
-                    QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
+                    QSTileViewImpl(themedContext)
                 )
             )
 
@@ -142,7 +141,7 @@
         qsPanel.tileLayout?.addTile(
             QSPanelControllerBase.TileRecord(
                 mock(QSTile::class.java),
-                QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
+                QSTileViewImpl(themedContext)
             )
         )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 71ea831..9bd3b79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -18,32 +18,33 @@
 
 import android.content.res.Configuration
 import android.testing.AndroidTestingRunner
+import android.view.ContextThemeWrapper
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.media.controls.ui.MediaHostState
 import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.QSTileView
 import com.android.systemui.qs.customize.QSCustomizerController
 import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.leak.RotationUtils
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.any
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -57,7 +58,6 @@
     @Mock private lateinit var qsLogger: QSLogger
     @Mock private lateinit var tile: QSTile
     @Mock private lateinit var tileLayout: TileLayout
-    @Mock private lateinit var tileView: QSTileView
     @Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
 
     private val uiEventLogger = UiEventLoggerFake()
@@ -75,7 +75,8 @@
         whenever(quickQSPanel.isAttachedToWindow).thenReturn(true)
         whenever(quickQSPanel.dumpableTag).thenReturn("")
         whenever(quickQSPanel.resources).thenReturn(mContext.resources)
-        whenever(qsHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
+        whenever(quickQSPanel.context)
+            .thenReturn(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
 
         controller =
             TestQuickQSPanelController(
@@ -88,7 +89,8 @@
                 metricsLogger,
                 uiEventLogger,
                 qsLogger,
-                dumpManager)
+                dumpManager
+            )
 
         controller.init()
     }
@@ -167,7 +169,9 @@
             metricsLogger,
             uiEventLogger,
             qsLogger,
-            dumpManager) {
+            dumpManager,
+            ResourcesSplitShadeStateController()
+        ) {
 
         private var rotation = RotationUtils.ROTATION_NONE
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
deleted file mode 100644
index ea8c64a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.QSTile.SlashState;
-import com.android.systemui.qs.tileimpl.SlashImageView;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class SlashImageViewTest extends SysuiTestCase {
-    private TestableSlashImageView mSlashView;
-
-    @Test
-    public void testSetNonNullSlashStateCreatesSlashDrawable() {
-        SlashState mockState = mock(SlashState.class);
-        Drawable mockDrawable = mock(Drawable.class);
-        mSlashView = new TestableSlashImageView(mContext);
-        assertTrue(mSlashView.getSlashDrawable() == null);
-
-        mSlashView.setState(mockState, mockDrawable);
-
-        assertTrue(mSlashView.getSlashDrawable() != null);
-    }
-
-    @Test
-    public void testSetNullSlashStateRemovesSlashDrawable() {
-        SlashState mockState = mock(SlashState.class);
-        Drawable mockDrawable = mock(Drawable.class);
-        mSlashView = new TestableSlashImageView(mContext);
-        mSlashView.setState(mockState, mockDrawable);
-
-        assertTrue(mSlashView.getSlashDrawable() != null);
-
-        mSlashView.setState(null, mockDrawable);
-
-        assertTrue(mSlashView.getSlashDrawable() == null);
-    }
-
-    @Test
-    public void testSetNullDrawableRemovesSlashDrawable() {
-        SlashState mockState = mock(SlashState.class);
-        Drawable mockDrawable = mock(Drawable.class);
-
-        mSlashView = new TestableSlashImageView(mContext);
-        mSlashView.setImageDrawable(mockDrawable);
-        mSlashView.setState(mockState, mockDrawable);
-        mSlashView.setImageDrawable(null);
-
-        assertTrue(mSlashView.getSlashDrawable() == null);
-    }
-
-    @Test
-    public void testSetImageDrawableUsesDrawableLevel() {
-        SlashImageView iv = new SlashImageView(mContext);
-        Drawable mockDrawable = mock(Drawable.class);
-        when(mockDrawable.getLevel()).thenReturn(2);
-
-        iv.setImageDrawable(mockDrawable);
-
-        // Make sure setting the drawable didn't reset its level to 0
-        verify(mockDrawable).setLevel(eq(2));
-    }
-
-    // Expose getSlashDrawable
-    private static class TestableSlashImageView extends SlashImageView {
-        TestableSlashImageView(Context c) {
-            super(c);
-        }
-
-        private SlashDrawable getSlashDrawable() {
-            return mSlash;
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index f55ef65..4a15d74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -42,7 +42,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
 import com.android.systemui.qs.tileimpl.QSTileViewImpl;
 
 import org.junit.Before;
@@ -78,7 +77,7 @@
     private QSPanelControllerBase.TileRecord createTileRecord() {
         return new QSPanelControllerBase.TileRecord(
                 mock(QSTile.class),
-                spy(new QSTileViewImpl(mSpyContext, new QSIconViewImpl(mSpyContext))));
+                spy(new QSTileViewImpl(mSpyContext)));
     }
 
     @Test
@@ -272,7 +271,7 @@
 
     private static class FakeTileView extends QSTileViewImpl {
         FakeTileView(Context context) {
-            super(context, new QSIconViewImpl(context), /* collapsed= */ false);
+            super(context, /* collapsed= */ false);
         }
 
         void changeState(QSTile.State state) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 78a0258..2eed38f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -34,7 +34,6 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
-import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -53,7 +52,6 @@
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
@@ -393,11 +391,6 @@
         }
 
         @Override
-        public QSIconView createTileView(Context context) {
-            return null;
-        }
-
-        @Override
         public void click(@Nullable View view) {}
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
index 013c925..7e0e7d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
@@ -16,10 +16,8 @@
 
 package com.android.systemui.qs.pipeline.domain.interactor
 
-import android.content.Context
 import android.view.View
 import com.android.internal.logging.InstanceId
-import com.android.systemui.plugins.qs.QSIconView
 import com.android.systemui.plugins.qs.QSTile
 
 class FakeQSTile(
@@ -58,10 +56,6 @@
         callbacks.clear()
     }
 
-    override fun createTileView(context: Context?): QSIconView? {
-        return null
-    }
-
     override fun click(view: View?) {}
 
     override fun secondaryClick(view: View?) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 180fed9..81a9604 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -49,7 +49,6 @@
 public class QSIconViewImplTest extends SysuiTestCase {
 
     private QSIconViewImpl mIconView;
-    private static int RES_ID = 1;
 
     @Before
     public void setup() {
@@ -79,7 +78,7 @@
 
     @Test
     public void testMutateIconDrawable() {
-        SlashImageView iv = mock(SlashImageView.class);
+        ImageView iv = mock(ImageView.class);
         Drawable originalDrawable = mock(Drawable.class);
         Drawable otherDrawable = mock(Drawable.class);
         State s = new State();
@@ -92,7 +91,7 @@
 
         mIconView.updateIcon(iv, s, /* allowAnimations= */true);
 
-        verify(iv).setState(any(), eq(otherDrawable));
+        verify(iv).setImageDrawable(eq(otherDrawable));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 3c66772..780c56c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -29,7 +29,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.qs.QSIconView
 import com.android.systemui.plugins.qs.QSTile
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -44,8 +43,6 @@
 class QSTileViewImplTest : SysuiTestCase() {
 
     @Mock
-    private lateinit var iconView: QSIconView
-    @Mock
     private lateinit var customDrawable: Drawable
 
     private lateinit var tileView: FakeTileView
@@ -57,7 +54,7 @@
         MockitoAnnotations.initMocks(this)
         context.ensureTestableResources()
 
-        tileView = FakeTileView(context, iconView, false)
+        tileView = FakeTileView(context, false)
         customDrawableView = tileView.requireViewById(R.id.customDrawable)
         chevronView = tileView.requireViewById(R.id.chevron)
     }
@@ -385,11 +382,9 @@
 
     class FakeTileView(
         context: Context,
-        icon: QSIconView,
         collapsed: Boolean
     ) : QSTileViewImpl(
             ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
-            icon,
             collapsed
     ) {
         fun changeState(state: QSTile.State) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index b00ae39..dd55bad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -395,7 +395,7 @@
                 PendingIntent.getActivity(mContext, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
         WalletCard walletCard =
                 new WalletCard.Builder(
-                    CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
+                        CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
         GetWalletCardsResponse response =
                 new GetWalletCardsResponse(Collections.singletonList(walletCard), 0);
 
@@ -410,6 +410,22 @@
     }
 
     @Test
+    public void testQueryCards_cardDataPayment_updateSideViewDrawable() {
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+        setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_PAYMENT);
+
+        assertNotNull(mTile.getState().sideViewCustomDrawable);
+    }
+
+    @Test
+    public void testQueryCards_cardDataNonPayment_updateSideViewDrawable() {
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+        setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_NON_PAYMENT);
+
+        assertNull(mTile.getState().sideViewCustomDrawable);
+    }
+
+    @Test
     public void testQueryCards_noCards_notUpdateSideViewDrawable() {
         setUpWalletCard(/* hasCard= */ false);
 
@@ -438,6 +454,29 @@
         verifyZeroInteractions(mQuickAccessWalletClient);
     }
 
+    private WalletCard createWalletCardWithType(Context context, int cardType) {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+        return new WalletCard.Builder(CARD_ID, cardType, CARD_IMAGE, CARD_DESCRIPTION,
+                pendingIntent).build();
+    }
+
+    private void setUpWalletCardWithType(boolean hasCard, int cardType) {
+        GetWalletCardsResponse response =
+                new GetWalletCardsResponse(
+                        hasCard
+                                ? Collections.singletonList(
+                                createWalletCardWithType(mContext, cardType))
+                                : Collections.EMPTY_LIST, 0);
+
+        mTile.handleSetListening(true);
+
+        verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+        mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+        mTestableLooper.processAllMessages();
+    }
+
     private void setUpWalletCard(boolean hasCard) {
         GetWalletCardsResponse response =
                 new GetWalletCardsResponse(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
new file mode 100644
index 0000000..47b4244
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
@@ -0,0 +1,41 @@
+package com.android.systemui.qs.tiles.base
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserActionHandler
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
+
+    @Mock private lateinit var activityStarted: ActivityStarter
+
+    lateinit var underTest: QSTileIntentUserActionHandler
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest = QSTileIntentUserActionHandler(activityStarted)
+    }
+
+    @Test
+    fun testPassesIntentToStarter() {
+        val intent = Intent("test.ACTION")
+
+        underTest.handle(QSTileUserAction.Click(context, null), intent)
+
+        verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
new file mode 100644
index 0000000..643866e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -0,0 +1,90 @@
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.graphics.drawable.Icon
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.MediumTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
+import com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// TODO(b/299909368): Add more tests
+@MediumTest
+@RoboPilotTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
+
+    private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
+    private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
+
+    private val testCoroutineDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testCoroutineDispatcher)
+
+    private lateinit var underTest: QSTileViewModel
+
+    @Before
+    fun setup() {
+        underTest = createViewModel(testScope)
+    }
+
+    @Test
+    fun testDoesntListenStateUntilCreated() =
+        testScope.runTest {
+            assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
+
+            underTest.onLifecycle(QSTileLifecycle.ALIVE)
+            underTest.onUserIdChanged(1)
+
+            assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
+
+            underTest.state.launchIn(backgroundScope)
+            runCurrent()
+
+            assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
+            assertThat(fakeQSTileDataInteractor.dataRequests.first())
+                .isEqualTo(QSTileDataRequest(1, StateUpdateTrigger.InitialRequest))
+        }
+
+    private fun createViewModel(
+        scope: TestScope,
+        config: QSTileConfig = TEST_QS_TILE_CONFIG,
+    ): QSTileViewModel =
+        object :
+            BaseQSTileViewModel<Any>(
+                config,
+                fakeQSTileUserActionInteractor,
+                fakeQSTileDataInteractor,
+                object : QSTileDataToStateMapper<Any> {
+                    override fun map(config: QSTileConfig, data: Any): QSTileState {
+                        return QSTileState(config.tileIcon, config.tileLabel)
+                    }
+                },
+                testCoroutineDispatcher,
+                tileScope = scope.backgroundScope,
+            ) {}
+
+    private companion object {
+        val TEST_QS_TILE_CONFIG =
+            QSTileConfig(
+                TileSpec.create("default"),
+                Icon.createWithContentUri(""),
+                "",
+            )
+    }
+}
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 e353a53..6d358db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -48,7 +48,6 @@
 import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_WAKING
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -94,8 +93,8 @@
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var shellInterface: ShellInterface
     @Mock private lateinit var navBarController: NavigationBarController
-    @Mock private lateinit var centralSurfaces: CentralSurfaces
     @Mock private lateinit var shadeViewController: ShadeViewController
+    @Mock private lateinit var screenPinningRequest: ScreenPinningRequest
     @Mock private lateinit var navModeController: NavigationModeController
     @Mock private lateinit var statusBarWinController: NotificationShadeWindowController
     @Mock private lateinit var userTracker: UserTracker
@@ -134,8 +133,8 @@
                 commandQueue,
                 shellInterface,
                 { navBarController },
-                { Optional.of(centralSurfaces) },
                 { shadeViewController },
+                screenPinningRequest,
                 navModeController,
                 statusBarWinController,
                 sysUiState,
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 141fcbb..9c8d14d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -99,6 +99,8 @@
             sceneInteractor = sceneInteractor,
         )
 
+    private val communalInteractor = utils.communalInteractor()
+
     private val transitionState =
         MutableStateFlow<ObservableTransitionState>(
             ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
@@ -123,7 +125,9 @@
 
     private val lockscreenSceneViewModel =
         LockscreenSceneViewModel(
+            applicationScope = testScope.backgroundScope,
             authenticationInteractor = authenticationInteractor,
+            communalInteractor = communalInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
@@ -432,6 +436,7 @@
                 fromScene = getCurrentSceneInUi(),
                 toScene = to.key,
                 progress = progressFlow,
+                isUserInputDriven = false,
             )
         runCurrent()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 181f8a7..ff28d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -119,6 +119,7 @@
                     fromScene = SceneKey.Lockscreen,
                     toScene = SceneKey.Shade,
                     progress = progress,
+                    isUserInputDriven = false,
                 )
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index ed716a9..afc0e69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -86,6 +86,7 @@
                     fromScene = SceneKey.Lockscreen,
                     toScene = SceneKey.Shade,
                     progress = progress,
+                    isUserInputDriven = false,
                 )
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
@@ -123,6 +124,7 @@
                     fromScene = underTest.desiredScene.value.key,
                     toScene = SceneKey.Shade,
                     progress = progress,
+                    isUserInputDriven = false,
                 )
             assertThat(transitionTo).isEqualTo(SceneKey.Shade)
 
@@ -158,7 +160,8 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Gone,
                         toScene = SceneKey.Lockscreen,
-                        progress = flowOf(0.5f)
+                        progress = flowOf(0.5f),
+                        isUserInputDriven = false,
                     )
                 )
             val transitioning by
@@ -176,7 +179,8 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.QuickSettings,
-                        progress = flowOf(0.5f)
+                        progress = flowOf(0.5f),
+                        isUserInputDriven = false,
                     )
                 )
             underTest.setTransitionState(transitionState)
@@ -192,7 +196,8 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.Lockscreen,
-                        progress = flowOf(0.5f)
+                        progress = flowOf(0.5f),
+                        isUserInputDriven = false,
                     )
                 )
             val transitioning by
@@ -219,7 +224,8 @@
                 ObservableTransitionState.Transition(
                     fromScene = SceneKey.Shade,
                     toScene = SceneKey.Lockscreen,
-                    progress = flowOf(0.5f)
+                    progress = flowOf(0.5f),
+                    isUserInputDriven = false,
                 )
             assertThat(transitioning).isTrue()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 145629a..16fdf8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -109,6 +109,7 @@
                     fromScene = SceneKey.Gone,
                     toScene = SceneKey.Shade,
                     progress = flowOf(0.5f),
+                    isUserInputDriven = false,
                 )
             assertThat(isVisible).isTrue()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -121,6 +122,7 @@
                     fromScene = SceneKey.Shade,
                     toScene = SceneKey.Gone,
                     progress = flowOf(0.5f),
+                    isUserInputDriven = false,
                 )
             assertThat(isVisible).isTrue()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 17ee3a1..f6195aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -78,8 +78,8 @@
         @JvmStatic
         fun testCases() = buildList {
             repeat(4) { combination ->
-                val isComposeAvailable = combination and 0b100 != 0
-                val areAllFlagsSet = combination and 0b001 != 0
+                val isComposeAvailable = combination and 0b10 != 0
+                val areAllFlagsSet = combination and 0b01 != 0
 
                 val expectedEnabled = isComposeAvailable && areAllFlagsSet
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
deleted file mode 100644
index 03d9444..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-
-/*
- * Copyright (C) 2022 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.screenrecord
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogLaunchAnimator
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserContextProvider
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class ScreenRecordDialogTest : SysuiTestCase() {
-
-    @Mock
-    private lateinit var starter: ActivityStarter
-    @Mock
-    private lateinit var controller: RecordingController
-    @Mock
-    private lateinit var userContextProvider: UserContextProvider
-    @Mock
-    private lateinit var flags: FeatureFlags
-    @Mock
-    private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
-    @Mock
-    private lateinit var onStartRecordingClicked: Runnable
-
-    private lateinit var dialog: ScreenRecordDialog
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        dialog = ScreenRecordDialog(
-            context, controller, starter, userContextProvider, flags, dialogLaunchAnimator,
-            onStartRecordingClicked
-        )
-    }
-
-    @After
-    fun teardown() {
-        if (::dialog.isInitialized) {
-            dialog.dismiss()
-        }
-    }
-
-    @Test
-    fun testShowDialog_partialScreenSharingDisabled_appButtonIsNotVisible() {
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
-
-        dialog.show()
-
-        val visibility = dialog.requireViewById<View>(R.id.button_app).visibility
-        assertThat(visibility).isEqualTo(View.GONE)
-    }
-
-    @Test
-    fun testShowDialog_partialScreenSharingEnabled_appButtonIsVisible() {
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-
-        dialog.show()
-
-        val visibility = dialog.requireViewById<View>(R.id.button_app).visibility
-        assertThat(visibility).isEqualTo(View.VISIBLE)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index ad6909d..a6c25be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -24,7 +24,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.ActivityStarter
@@ -36,8 +35,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -48,7 +47,6 @@
     @Mock private lateinit var controller: RecordingController
     @Mock private lateinit var userContextProvider: UserContextProvider
     @Mock private lateinit var flags: FeatureFlags
-    @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     @Mock private lateinit var onStartRecordingClicked: Runnable
 
     private lateinit var dialog: ScreenRecordPermissionDialog
@@ -63,7 +61,6 @@
                 UserHandle.of(0),
                 controller,
                 starter,
-                dialogLaunchAnimator,
                 userContextProvider,
                 onStartRecordingClicked
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index cfdf66e..a105c15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -180,7 +180,7 @@
         }
 
     @Test
-    fun executeScreenshots_doesNotReportFinishedIfOneFinishesOtherFails() =
+    fun executeScreenshots_oneFinishesOtherFails_reportFailsOnlyAtTheEnd() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
             val onSaved = { _: Uri -> }
@@ -207,7 +207,7 @@
         }
 
     @Test
-    fun executeScreenshots_doesNotReportFinishedAfterOneFails() =
+    fun executeScreenshots_allDisplaysFail_reportsFail() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
             val onSaved = { _: Uri -> }
@@ -224,11 +224,12 @@
             capturer0.value.reportError()
 
             verify(callback, never()).onFinish()
-            verify(callback).reportError()
+            verify(callback, never()).reportError()
 
-            capturer1.value.onFinish()
+            capturer1.value.reportError()
 
             verify(callback, never()).onFinish()
+            verify(callback).reportError()
             screenshotExecutor.onDestroy()
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index beb981d..1bb2ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -11,10 +11,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executor
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,6 +63,9 @@
     @Mock private lateinit var callback: UserTracker.Callback
     @Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
 
+    private val fakeFeatures = FakeFeatureFlagsClassic()
+    private val testDispatcher = StandardTestDispatcher()
+
     private lateinit var tracker: UserTrackerImpl
 
     @Before
@@ -68,12 +74,21 @@
 
         `when`(context.user).thenReturn(UserHandle.SYSTEM)
         `when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
-
-        tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
     }
 
     @Test
-    fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() {
+    fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() = runTest {
+        tracker =
+            UserTrackerImpl(
+                context,
+                { fakeFeatures },
+                userManager,
+                iActivityManager,
+                dumpManager,
+                this,
+                testDispatcher,
+                handler
+            )
         tracker.initialize(0)
         tracker.addCallback(callback, executor)
         val profileID = tracker.userId + 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index aa98f08..52b25f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -26,16 +26,25 @@
 import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Executor
+import com.google.common.truth.TruthJUnit.assume
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
@@ -43,28 +52,52 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
 
+
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(Parameterized::class)
 class UserTrackerImplTest : SysuiTestCase() {
 
+    companion object {
+
+        @JvmStatic
+        @Parameterized.Parameters
+        fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false)
+    }
+
     @Mock
     private lateinit var context: Context
+
     @Mock
     private lateinit var userManager: UserManager
+
     @Mock
     private lateinit var iActivityManager: IActivityManager
+
     @Mock
     private lateinit var userSwitchingReply: IRemoteCallback
+
     @Mock(stubOnly = true)
     private lateinit var dumpManager: DumpManager
+
     @Mock(stubOnly = true)
     private lateinit var handler: Handler
 
+    @Parameterized.Parameter
+    @JvmField
+    var isBackgroundUserTrackerEnabled: Boolean = false
+
+    private val testScope = TestScope()
+    private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
     private val executor = Executor(Runnable::run)
+    private val featureFlags = FakeFeatureFlagsClassic()
+
     private lateinit var tracker: UserTrackerImpl
 
     @Before
@@ -84,54 +117,65 @@
             listOf(info)
         }
 
-        tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
+        featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled)
+        tracker =
+                UserTrackerImpl(
+                        context,
+                        { featureFlags },
+                        userManager,
+                        iActivityManager,
+                        dumpManager,
+                        testScope.backgroundScope,
+                        testDispatcher,
+                        handler,
+                )
     }
 
     @Test
-    fun testNotInitialized() {
+    fun testNotInitialized() = testScope.runTest {
         assertThat(tracker.initialized).isFalse()
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testGetUserIdBeforeInitThrowsException() {
+    fun testGetUserIdBeforeInitThrowsException() = testScope.runTest {
         tracker.userId
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testGetUserHandleBeforeInitThrowsException() {
+    fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest {
         tracker.userHandle
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testGetUserContextBeforeInitThrowsException() {
+    fun testGetUserContextBeforeInitThrowsException() = testScope.runTest {
         tracker.userContext
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testGetUserContentResolverBeforeInitThrowsException() {
+    fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest {
         tracker.userContentResolver
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testGetUserProfilesBeforeInitThrowsException() {
+    fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest {
         tracker.userProfiles
     }
 
     @Test
-    fun testInitialize() {
+    fun testInitialize() = testScope.runTest {
         tracker.initialize(0)
 
         assertThat(tracker.initialized).isTrue()
     }
 
     @Test
-    fun testReceiverRegisteredOnInitialize() {
+    fun testReceiverRegisteredOnInitialize() = testScope.runTest {
         tracker.initialize(0)
 
         val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
 
-        verify(context).registerReceiverForAllUsers(
-                eq(tracker), capture(captor), isNull(), eq(handler))
+        verify(context)
+                .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
         with(captor.value) {
             assertThat(countActions()).isEqualTo(6)
             assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
@@ -144,7 +188,7 @@
     }
 
     @Test
-    fun testInitialValuesSet() {
+    fun testInitialValuesSet() = testScope.runTest {
         val testID = 4
         tracker.initialize(testID)
 
@@ -161,7 +205,7 @@
     }
 
     @Test
-    fun testUserSwitch() {
+    fun testUserSwitch() = testScope.runTest {
         tracker.initialize(0)
         val newID = 5
 
@@ -169,6 +213,7 @@
         verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
         captor.value.onBeforeUserSwitching(newID)
         captor.value.onUserSwitching(newID, userSwitchingReply)
+        runCurrent()
         verify(userSwitchingReply).sendResult(any())
 
         verify(userManager).getProfiles(newID)
@@ -184,7 +229,7 @@
     }
 
     @Test
-    fun testManagedProfileAvailable() {
+    fun testManagedProfileAvailable() = testScope.runTest {
         tracker.initialize(0)
         val profileID = tracker.userId + 10
 
@@ -209,7 +254,8 @@
         assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
     }
 
-    fun testManagedProfileUnavailable() {
+    @Test
+    fun testManagedProfileUnavailable() = testScope.runTest {
         tracker.initialize(0)
         val profileID = tracker.userId + 10
 
@@ -234,7 +280,8 @@
         assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
     }
 
-    fun testManagedProfileStartedAndRemoved() {
+    @Test
+    fun testManagedProfileStartedAndRemoved() = testScope.runTest {
         tracker.initialize(0)
         val profileID = tracker.userId + 10
 
@@ -271,7 +318,7 @@
     }
 
     @Test
-    fun testCallbackNotCalledOnAdd() {
+    fun testCallbackNotCalledOnAdd() = testScope.runTest {
         tracker.initialize(0)
         val callback = TestCallback()
 
@@ -282,7 +329,7 @@
     }
 
     @Test
-    fun testCallbackCalledOnUserChanging() {
+    fun testCallbackCalledOnUserChanging() = testScope.runTest {
         tracker.initialize(0)
         val callback = TestCallback()
         tracker.addCallback(callback, executor)
@@ -293,15 +340,49 @@
         verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
         captor.value.onBeforeUserSwitching(newID)
         captor.value.onUserSwitching(newID, userSwitchingReply)
-        verify(userSwitchingReply).sendResult(any())
+        runCurrent()
 
+        verify(userSwitchingReply).sendResult(any())
         assertThat(callback.calledOnUserChanging).isEqualTo(1)
         assertThat(callback.lastUser).isEqualTo(newID)
         assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
     }
 
     @Test
-    fun testCallbackCalledOnUserChanged() {
+    fun testAsyncCallbackWaitsUserToChange() = testScope.runTest {
+        // Skip this test for CountDownLatch variation. The problem is that there would be a
+        // deadlock if the callbacks processing runs on the same thread as the callback (which
+        // is blocked by the latch). Before the change it works because the callbacks are
+        // processed on a binder thread which is always distinct.
+        // This is the issue that this feature addresses.
+        assume().that(isBackgroundUserTrackerEnabled).isTrue()
+
+        tracker.initialize(0)
+        val callback = TestCallback()
+        val callbackExecutor = FakeExecutor(FakeSystemClock())
+        tracker.addCallback(callback, callbackExecutor)
+
+        val newID = 5
+
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onBeforeUserSwitching(newID)
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+
+        assertThat(callback.calledOnUserChanging).isEqualTo(0)
+        verify(userSwitchingReply, never()).sendResult(any())
+
+        FakeExecutor.exhaustExecutors(callbackExecutor)
+        runCurrent()
+        FakeExecutor.exhaustExecutors(callbackExecutor)
+        runCurrent()
+
+        assertThat(callback.calledOnUserChanging).isEqualTo(1)
+        verify(userSwitchingReply).sendResult(any())
+    }
+
+    @Test
+    fun testCallbackCalledOnUserChanged() = testScope.runTest {
         tracker.initialize(0)
         val callback = TestCallback()
         tracker.addCallback(callback, executor)
@@ -312,6 +393,7 @@
         verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
         captor.value.onBeforeUserSwitching(newID)
         captor.value.onUserSwitchComplete(newID)
+        runCurrent()
 
         assertThat(callback.calledOnUserChanged).isEqualTo(1)
         assertThat(callback.lastUser).isEqualTo(newID)
@@ -321,7 +403,7 @@
     }
 
     @Test
-    fun testCallbackCalledOnUserInfoChanged() {
+    fun testCallbackCalledOnUserInfoChanged() = testScope.runTest {
         tracker.initialize(0)
         val callback = TestCallback()
         tracker.addCallback(callback, executor)
@@ -331,18 +413,18 @@
             val id = invocation.getArgument<Int>(0)
             val info = UserInfo(id, "", UserInfo.FLAG_FULL)
             val infoProfile = UserInfo(
-                id + 10,
-                "",
-                "",
-                UserInfo.FLAG_MANAGED_PROFILE,
-                UserManager.USER_TYPE_PROFILE_MANAGED
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
             )
             infoProfile.profileGroupId = id
             listOf(info, infoProfile)
         }
 
         val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
-            .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
 
         tracker.onReceive(context, intent)
 
@@ -352,7 +434,7 @@
     }
 
     @Test
-    fun testCallbackRemoved() {
+    fun testCallbackRemoved() = testScope.runTest {
         tracker.initialize(0)
         val newID = 5
         val profileID = newID + 10
@@ -364,6 +446,7 @@
         val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
         verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
         captor.value.onUserSwitching(newID, userSwitchingReply)
+        runCurrent()
         verify(userSwitchingReply).sendResult(any())
         captor.value.onUserSwitchComplete(newID)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 4f57fd0..9130bc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -120,6 +120,7 @@
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.data.repository.ShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.shade.transition.ShadeTransitionController;
@@ -172,6 +173,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.util.kotlin.JavaAdapter;
@@ -320,7 +322,6 @@
             mEmptySpaceClickListenerCaptor;
     @Mock protected ActivityStarter mActivityStarter;
     @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
-    @Mock protected ShadeRepository mShadeRepository;
     @Mock private ShadeInteractor mShadeInteractor;
     @Mock private JavaAdapter mJavaAdapter;
     @Mock private CastController mCastController;
@@ -341,6 +342,7 @@
     protected Handler mMainHandler;
     protected View.OnLayoutChangeListener mLayoutChangeListener;
     protected KeyguardStatusViewController mKeyguardStatusViewController;
+    protected ShadeRepository mShadeRepository;
 
     protected final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
     protected final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
@@ -362,6 +364,7 @@
         mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
         mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
         mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
+        mShadeRepository = new FakeShadeRepository();
 
         SystemClock systemClock = new FakeSystemClock();
         mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
@@ -659,7 +662,7 @@
                 mSharedNotificationContainerInteractor,
                 mKeyguardViewConfigurator,
                 mKeyguardFaceAuthInteractor,
-                mKeyguardRootView);
+                new ResourcesSplitShadeStateController());
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
                 null,
@@ -731,7 +734,8 @@
                 mShadeRepository,
                 mShadeInteractor,
                 mJavaAdapter,
-                mCastController
+                mCastController,
+                new ResourcesSplitShadeStateController()
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 7aeafeb..911cab4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade;
 
-import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -59,6 +58,7 @@
 import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.R;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.shared.model.WakeSleepReason;
 import com.android.systemui.keyguard.shared.model.WakefulnessModel;
 import com.android.systemui.keyguard.shared.model.WakefulnessState;
@@ -197,6 +197,7 @@
 
     @Test
     public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() {
+        enableSplitShade(/* enabled= */ false);
         setBottomPadding(/* stackScrollLayoutBottom= */ 100,
                 /* lockIconPadding= */ 20,
                 /* indicationPadding= */ 0,
@@ -209,6 +210,7 @@
 
     @Test
     public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() {
+        enableSplitShade(/* enabled= */ false);
         setBottomPadding(/* stackScrollLayoutBottom= */ 100,
                 /* lockIconPadding= */ 0,
                 /* indicationPadding= */ 30,
@@ -221,6 +223,7 @@
 
     @Test
     public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() {
+        enableSplitShade(/* enabled= */ false);
         setBottomPadding(/* stackScrollLayoutBottom= */ 100,
                 /* lockIconPadding= */ 0,
                 /* indicationPadding= */ 0,
@@ -233,6 +236,7 @@
 
     @Test
     public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() {
+        enableSplitShade(/* enabled= */ false);
         setBottomPadding(/* stackScrollLayoutBottom= */ 100,
                 /* lockIconPadding= */ 10,
                 /* indicationPadding= */ 8,
@@ -244,6 +248,19 @@
     }
 
     @Test
+    public void getVerticalSpaceForLockscreenShelf_splitShade() {
+        enableSplitShade(/* enabled= */ true);
+        setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+                /* lockIconPadding= */ 10,
+                /* indicationPadding= */ 8,
+                /* ambientPadding= */ 0);
+
+        when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+        assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+                .isEqualTo(0);
+    }
+
+    @Test
     public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() {
         mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
                 mNotificationPanelViewController.getMaxPanelHeight() / 2);
@@ -817,6 +834,42 @@
     }
 
     @Test
+    public void switchesToBigClockInSplitShadeOn_landFlagOn_ForceSmallClock() {
+        when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ false);
+        mNotificationPanelViewController.setDozing(false, false);
+        when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(true);
+        when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
+        when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        clearInvocations(mKeyguardStatusViewController);
+
+        enableSplitShade(/* enabled= */ true);
+        mNotificationPanelViewController.updateResources();
+
+        verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ false);
+    }
+
+    @Test
+    public void switchesToBigClockInSplitShadeOn_landFlagOff_DontForceSmallClock() {
+        when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ false);
+        mNotificationPanelViewController.setDozing(false, false);
+        when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(false);
+        when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
+        when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        clearInvocations(mKeyguardStatusViewController);
+
+        enableSplitShade(/* enabled= */ true);
+        mNotificationPanelViewController.updateResources();
+
+        verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
+    }
+
+    @Test
     public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
@@ -866,37 +919,6 @@
     }
 
     @Test
-    public void testUnlockAnimationDoesNotAffectScrim() {
-        mNotificationPanelViewController.onUnlockHintStarted();
-        verify(mScrimController).setExpansionAffectsAlpha(false);
-        mNotificationPanelViewController.onUnlockHintFinished();
-        verify(mScrimController).setExpansionAffectsAlpha(true);
-    }
-
-    @Test
-    public void testUnlockHintAnimation_runs_whenNotInPowerSaveMode_andDozeAmountIsZero() {
-        when(mPowerManager.isPowerSaveMode()).thenReturn(false);
-        when(mAmbientState.getDozeAmount()).thenReturn(0f);
-        mNotificationPanelViewController.startUnlockHintAnimation();
-        assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isTrue();
-    }
-
-    @Test
-    public void testUnlockHintAnimation_doesNotRun_inPowerSaveMode() {
-        when(mPowerManager.isPowerSaveMode()).thenReturn(true);
-        mNotificationPanelViewController.startUnlockHintAnimation();
-        assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse();
-    }
-
-    @Test
-    public void testUnlockHintAnimation_doesNotRun_whenDozeAmountNotZero() {
-        when(mPowerManager.isPowerSaveMode()).thenReturn(false);
-        when(mAmbientState.getDozeAmount()).thenReturn(0.5f);
-        mNotificationPanelViewController.startUnlockHintAnimation();
-        assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse();
-    }
-
-    @Test
     public void setKeyguardStatusBarAlpha_setsAlphaOnKeyguardStatusBarController() {
         float statusBarAlpha = 0.5f;
 
@@ -939,8 +961,10 @@
 
     @Test
     public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+
         // to make sure shade is in expanded state
-        mNotificationPanelViewController.startWaitingForExpandGesture();
+        mNotificationPanelViewController.startInputFocusTransfer();
 
         // switch to split shade from portrait (default state)
         enableSplitShade(/* enabled= */ true);
@@ -1056,36 +1080,6 @@
     }
 
     @Test
-    public void onEmptySpaceClicked_notDozingAndFaceDetectionIsNotRunning_startsUnlockAnimation() {
-        StatusBarStateController.StateListener statusBarStateListener =
-                mNotificationPanelViewController.getStatusBarStateListener();
-        statusBarStateListener.onStateChanged(KEYGUARD);
-        mNotificationPanelViewController.setDozing(false, false);
-        when(mUpdateMonitor.requestFaceAuth(NOTIFICATION_PANEL_CLICKED)).thenReturn(false);
-
-        // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
-        mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
-        mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
-
-        verify(mNotificationStackScrollLayoutController).setUnlockHintRunning(true);
-    }
-
-    @Test
-    public void onEmptySpaceClicked_notDozingAndFaceDetectionIsRunning_doesNotStartUnlockHint() {
-        StatusBarStateController.StateListener statusBarStateListener =
-                mNotificationPanelViewController.getStatusBarStateListener();
-        statusBarStateListener.onStateChanged(KEYGUARD);
-        mNotificationPanelViewController.setDozing(false, false);
-        when(mUpdateMonitor.requestFaceAuth(NOTIFICATION_PANEL_CLICKED)).thenReturn(true);
-
-        // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
-        mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
-        mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
-
-        verify(mNotificationStackScrollLayoutController, never()).setUnlockHintRunning(true);
-    }
-
-    @Test
     public void onEmptySpaceClicked_whenDozingAndOnKeyguard_doesNotRequestFaceAuth() {
         StatusBarStateController.StateListener statusBarStateListener =
                 mNotificationPanelViewController.getStatusBarStateListener();
@@ -1108,7 +1102,18 @@
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
         verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
+    }
 
+    @Test
+    public void nsslFlagEnabled_allowOnlyExternalTouches() {
+        when(mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)).thenReturn(true);
+
+        // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
+        mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
+        verify(mQsController, never()).disallowTouches();
+
+        mNotificationPanelViewController.handleExternalInterceptTouch(mDownMotionEvent);
+        verify(mQsController).disallowTouches();
     }
 
     @Test
@@ -1232,18 +1237,50 @@
     }
 
     @Test
-    public void shadeExpanded_whenWaitingForExpandGesture() {
-        mNotificationPanelViewController.startWaitingForExpandGesture();
+    public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
+        when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
         assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
     }
 
     @Test
-    public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
-        when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
+    public void shadeExpanded_whenInputFocusTransferStarted() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+
+        mNotificationPanelViewController.startInputFocusTransfer();
+
         assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
     }
 
     @Test
+    public void shadeNotExpanded_whenInputFocusTransferStartedButPanelsDisabled() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(false);
+
+        mNotificationPanelViewController.startInputFocusTransfer();
+
+        assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
+    }
+
+    @Test
+    public void cancelInputFocusTransfer_shadeCollapsed() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        mNotificationPanelViewController.startInputFocusTransfer();
+
+        mNotificationPanelViewController.cancelInputFocusTransfer();
+
+        assertThat(mNotificationPanelViewController.isExpanded()).isFalse();
+    }
+
+    @Test
+    public void finishInputFocusTransfer_shadeFlingingOpen() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        mNotificationPanelViewController.startInputFocusTransfer();
+
+        mNotificationPanelViewController.finishInputFocusTransfer(/* velocity= */ 0f);
+
+        assertThat(mNotificationPanelViewController.isFlinging()).isTrue();
+    }
+
+    @Test
     public void getFalsingThreshold_deviceNotInteractive_isQsThreshold() {
         mFakeKeyguardRepository.setWakefulnessModel(
                 new WakefulnessModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 39fe498..3da5f6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.log.BouncerLogger
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.statusbar.DragDownHelper
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationInsetsController
 import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -124,6 +125,7 @@
     private lateinit var unfoldTransitionProgressProvider:
         Optional<UnfoldTransitionProgressProvider>
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    @Mock lateinit var dragDownHelper: DragDownHelper
     @Mock
     lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
     @Mock lateinit var keyEventInteractor: KeyEventInteractor
@@ -137,6 +139,8 @@
 
     private lateinit var testScope: TestScope
 
+    private lateinit var featureFlags: FakeFeatureFlags
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -150,12 +154,13 @@
         whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
             .thenReturn(emptyFlow<TransitionStep>())
 
-        val featureFlags = FakeFeatureFlags()
+        featureFlags = FakeFeatureFlags()
         featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
         featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+        featureFlags.set(Flags.MIGRATE_NSSL, false)
 
         testScope = TestScope()
         fakeClock = FakeSystemClock()
@@ -206,6 +211,7 @@
                 keyEventInteractor,
             )
         underTest.setupExpandedStatusBar()
+        underTest.setDragDownHelper(dragDownHelper)
 
         interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
         verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
@@ -347,9 +353,8 @@
         testScope.runTest {
             // GIVEN touch dispatcher in a state that returns true
             underTest.setStatusBarViewController(phoneStatusBarViewController)
-            whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(
-                true
-            )
+            whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+                .thenReturn(true)
             assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
 
             // WHEN launch animation is running for 2 seconds
@@ -381,6 +386,32 @@
     }
 
     @Test
+    fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() {
+        // GIVEN not dozing
+        whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false)
+        // AND alternate bouncer doesn't want the touch
+        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+            .thenReturn(false)
+        // AND the lock icon doesn't want the touch
+        whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+        // AND the notification panel can accept touches
+        whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true)
+        whenever(dragDownHelper.isDragDownEnabled).thenReturn(true)
+        whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
+
+        // AND the drag down helper doesn't want the touch (to pull the shade down)
+        whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+
+        featureFlags.set(Flags.MIGRATE_NSSL, true)
+
+        // WHEN asked if should intercept touch
+        interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
+
+        // Verify that NPVC gets a chance to use the touch
+        verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
+    }
+
+    @Test
     fun testGetKeyguardMessageArea() =
         testScope.runTest {
             underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 3031658..04c4b45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -164,6 +164,7 @@
         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+        featureFlags.set(Flags.MIGRATE_NSSL, false)
         testScope = TestScope()
         controller =
             NotificationShadeWindowViewController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 577b6e0..e9e6d31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.whenever
@@ -100,7 +101,11 @@
         MockitoAnnotations.initMocks(this)
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
-        featureFlags = FakeFeatureFlags().apply { set(Flags.MIGRATE_NSSL, false) }
+        featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.MIGRATE_NSSL, false)
+                set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, false)
+            }
         mContext.ensureTestableResources()
         whenever(view.context).thenReturn(mContext)
         whenever(view.resources).thenReturn(mContext.resources)
@@ -118,6 +123,7 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
+                ResourcesSplitShadeStateController()
             )
 
         overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -474,6 +480,7 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
+                ResourcesSplitShadeStateController()
             )
         controller.updateConstraints()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 405199e..6801f2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.whenever
@@ -99,7 +100,11 @@
         MockitoAnnotations.initMocks(this)
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
-        featureFlags = FakeFeatureFlags().apply { set(Flags.MIGRATE_NSSL, true) }
+        featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.MIGRATE_NSSL, true)
+                set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, true)
+            }
         mContext.ensureTestableResources()
         whenever(view.context).thenReturn(mContext)
         whenever(view.resources).thenReturn(mContext.resources)
@@ -117,6 +122,7 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
+                ResourcesSplitShadeStateController()
             )
 
         overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -457,6 +463,7 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
+                ResourcesSplitShadeStateController()
             )
         controller.updateConstraints()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 46b636b..849127e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -75,6 +75,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.user.domain.interactor.UserInteractor;
 import com.android.systemui.util.kotlin.JavaAdapter;
@@ -182,7 +183,8 @@
                         mUserInteractor,
                         new SharedNotificationContainerInteractor(
                                 new FakeConfigurationRepository(),
-                                mContext),
+                                mContext,
+                                new ResourcesSplitShadeStateController()),
                         mShadeRepository
                 );
 
@@ -260,7 +262,8 @@
                 mShadeRepository,
                 mShadeInteractor,
                 new JavaAdapter(mTestScope.getBackgroundScope()),
-                mCastController
+                mCastController,
+                new ResourcesSplitShadeStateController()
         );
         mQsController.init();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 8f8b840..7d5f68e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -130,7 +130,7 @@
     var viewVisibility = View.GONE
     var viewAlpha = 1f
 
-    private val systemIcons = LinearLayout(context)
+    private val systemIconsHoverContainer = LinearLayout(context)
     private lateinit var shadeHeaderController: ShadeHeaderController
     private lateinit var carrierIconSlots: List<String>
     private val configurationController = FakeConfigurationController()
@@ -150,7 +150,8 @@
             .thenReturn(batteryMeterView)
 
         whenever<StatusIconContainer>(view.requireViewById(R.id.statusIcons)).thenReturn(statusIcons)
-        whenever<View>(view.requireViewById(R.id.shade_header_system_icons)).thenReturn(systemIcons)
+        whenever<View>(view.requireViewById(R.id.hover_system_icons_container))
+            .thenReturn(systemIconsHoverContainer)
 
         viewContext = Mockito.spy(context)
         whenever(view.context).thenReturn(viewContext)
@@ -457,12 +458,12 @@
     }
 
     @Test
-    fun testLargeScreenActive_collapseActionRun_onSystemIconsClick() {
+    fun testLargeScreenActive_collapseActionRun_onSystemIconsHoverContainerClick() {
         shadeHeaderController.largeScreenActive = true
         var wasRun = false
         shadeHeaderController.shadeCollapseAction = Runnable { wasRun = true }
 
-        systemIcons.performClick()
+        systemIconsHoverContainer.performClick()
 
         assertThat(wasRun).isTrue()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index dd6ab73..342b1c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
@@ -85,6 +86,7 @@
         SharedNotificationContainerInteractor(
             configurationRepository,
             mContext,
+            ResourcesSplitShadeStateController()
         )
 
     @Mock private lateinit var manager: UserManager
@@ -400,6 +402,7 @@
             assertThat(actual).isEqualTo(0.8f)
         }
 
+    @Test
     fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
         testScope.runTest {
             val actual by collectLastValue(underTest.shadeExpansion)
@@ -408,27 +411,31 @@
             keyguardRepository.setStatusBarState(StatusBarState.SHADE)
             overrideResource(R.bool.config_use_split_notification_shade, true)
             configurationRepository.onAnyConfigurationChange()
-            runCurrent()
             shadeRepository.setQsExpansion(.5f)
             shadeRepository.setLegacyShadeExpansion(.7f)
+            runCurrent()
 
             // THEN legacy shade expansion is passed through
             assertThat(actual).isEqualTo(.7f)
         }
 
+    @Test
     fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() =
         testScope.runTest {
             val actual by collectLastValue(underTest.shadeExpansion)
 
             // WHEN split shade is not enabled and QS is expanded
             keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            overrideResource(R.bool.config_use_split_notification_shade, false)
             shadeRepository.setQsExpansion(.5f)
             shadeRepository.setLegacyShadeExpansion(1f)
+            runCurrent()
 
             // THEN shade expansion is zero
             assertThat(actual).isEqualTo(0f)
         }
 
+    @Test
     fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() =
         testScope.runTest {
             val actual by collectLastValue(underTest.shadeExpansion)
@@ -469,7 +476,7 @@
     @Test
     fun expanding_shadeDraggedDown_expandingTrue() =
         testScope.runTest() {
-            val actual by collectLastValue(underTest.anyExpanding)
+            val actual by collectLastValue(underTest.isAnyExpanding)
 
             // GIVEN shade and QS collapsed
             shadeRepository.setLegacyShadeExpansion(0f)
@@ -487,7 +494,7 @@
     @Test
     fun expanding_qsDraggedDown_expandingTrue() =
         testScope.runTest() {
-            val actual by collectLastValue(underTest.anyExpanding)
+            val actual by collectLastValue(underTest.isAnyExpanding)
 
             // GIVEN shade and QS collapsed
             shadeRepository.setLegacyShadeExpansion(0f)
@@ -505,7 +512,7 @@
     @Test
     fun expanding_shadeDraggedUpAndDown() =
         testScope.runTest() {
-            val actual by collectLastValue(underTest.anyExpanding)
+            val actual by collectLastValue(underTest.isAnyExpanding)
 
             // WHEN shade starts collapsed then partially expanded
             shadeRepository.setLegacyShadeExpansion(0f)
@@ -530,7 +537,7 @@
             // THEN anyExpanding is still true
             assertThat(actual).isTrue()
 
-            // WHEN shade fully shadeExpanded
+            // WHEN shade fully expanded
             shadeRepository.setLegacyShadeExpansion(1f)
             runCurrent()
 
@@ -548,7 +555,7 @@
     @Test
     fun expanding_shadeDraggedDownThenUp_expandingFalse() =
         testScope.runTest() {
-            val actual by collectLastValue(underTest.anyExpanding)
+            val actual by collectLastValue(underTest.isAnyExpanding)
 
             // GIVEN shade starts collapsed
             shadeRepository.setLegacyShadeExpansion(0f)
@@ -616,6 +623,7 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
+                        isUserInputDriven = false,
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -652,6 +660,7 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
+                        isUserInputDriven = false,
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -687,6 +696,7 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = SceneKey.Shade,
                         progress = progress,
+                        isUserInputDriven = false,
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -706,4 +716,415 @@
             // THEN expansion is still 0
             assertThat(expansionAmount).isEqualTo(0f)
         }
+
+    @Test
+    fun userInteractingWithShade_shadeDraggedUpAndDown() =
+        testScope.runTest() {
+            val actual by collectLastValue(underTest.isUserInteractingWithShade)
+            // GIVEN shade collapsed and not tracking input
+            shadeRepository.setLegacyShadeExpansion(0f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+
+            // WHEN shade tracking starts
+            shadeRepository.setLegacyShadeTracking(true)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade dragged down halfway
+            shadeRepository.setLegacyShadeExpansion(.5f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade fully expanded but tracking is not stopped
+            shadeRepository.setLegacyShadeExpansion(1f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade fully collapsed but tracking is not stopped
+            shadeRepository.setLegacyShadeExpansion(0f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade dragged halfway and tracking is stopped
+            shadeRepository.setLegacyShadeExpansion(.6f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade completes expansion stopped
+            shadeRepository.setLegacyShadeExpansion(1f)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun userInteractingWithShade_shadeExpanded() =
+        testScope.runTest() {
+            val actual by collectLastValue(underTest.isUserInteractingWithShade)
+            // GIVEN shade collapsed and not tracking input
+            shadeRepository.setLegacyShadeExpansion(0f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+
+            // WHEN shade tracking starts
+            shadeRepository.setLegacyShadeTracking(true)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade dragged down halfway
+            shadeRepository.setLegacyShadeExpansion(.5f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade fully expanded and tracking is stopped
+            shadeRepository.setLegacyShadeExpansion(1f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun userInteractingWithShade_shadePartiallyExpanded() =
+        testScope.runTest() {
+            val actual by collectLastValue(underTest.isUserInteractingWithShade)
+            // GIVEN shade collapsed and not tracking input
+            shadeRepository.setLegacyShadeExpansion(0f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+
+            // WHEN shade tracking starts
+            shadeRepository.setLegacyShadeTracking(true)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade partially expanded
+            shadeRepository.setLegacyShadeExpansion(.4f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN tracking is stopped
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade goes back to collapsed
+            shadeRepository.setLegacyShadeExpansion(0f)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun userInteractingWithShade_shadeCollapsed() =
+        testScope.runTest() {
+            val actual by collectLastValue(underTest.isUserInteractingWithShade)
+            // GIVEN shade expanded and not tracking input
+            shadeRepository.setLegacyShadeExpansion(1f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+
+            // WHEN shade tracking starts
+            shadeRepository.setLegacyShadeTracking(true)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade dragged up halfway
+            shadeRepository.setLegacyShadeExpansion(.5f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN shade fully collapsed and tracking is stopped
+            shadeRepository.setLegacyShadeExpansion(0f)
+            shadeRepository.setLegacyShadeTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun userInteractingWithQs_qsDraggedUpAndDown() =
+        testScope.runTest() {
+            val actual by collectLastValue(underTest.isUserInteractingWithQs)
+            // GIVEN qs collapsed and not tracking input
+            shadeRepository.setQsExpansion(0f)
+            shadeRepository.setLegacyQsTracking(false)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+
+            // WHEN qs tracking starts
+            shadeRepository.setLegacyQsTracking(true)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN qs dragged down halfway
+            shadeRepository.setQsExpansion(.5f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN qs fully expanded but tracking is not stopped
+            shadeRepository.setQsExpansion(1f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN qs fully collapsed but tracking is not stopped
+            shadeRepository.setQsExpansion(0f)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN qs dragged halfway and tracking is stopped
+            shadeRepository.setQsExpansion(.6f)
+            shadeRepository.setLegacyQsTracking(false)
+            runCurrent()
+
+            // THEN user is interacting
+            assertThat(actual).isTrue()
+
+            // WHEN qs completes expansion stopped
+            shadeRepository.setQsExpansion(1f)
+            runCurrent()
+
+            // THEN user is not interacting
+            assertThat(actual).isFalse()
+        }
+    @Test
+    fun userInteracting_idle() =
+        testScope.runTest() {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = SceneKey.Shade
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is idle
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+        }
+
+    @Test
+    fun userInteracting_transitioning_toScene_programmatic() =
+        testScope.runTest() {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = SceneKey.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = SceneKey.Lockscreen,
+                        toScene = key,
+                        progress = progress,
+                        isUserInputDriven = false,
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+        }
+
+    @Test
+    fun userInteracting_transitioning_toScene_userInputDriven() =
+        testScope.runTest() {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = SceneKey.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = SceneKey.Lockscreen,
+                        toScene = key,
+                        progress = progress,
+                        isUserInputDriven = true,
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is true
+            assertThat(interacting).isTrue()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is true
+            assertThat(interacting).isTrue()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is true
+            assertThat(interacting).isTrue()
+        }
+
+    @Test
+    fun userInteracting_transitioning_fromScene_programmatic() =
+        testScope.runTest() {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = SceneKey.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = key,
+                        toScene = SceneKey.Lockscreen,
+                        progress = progress,
+                        isUserInputDriven = false,
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+        }
+
+    @Test
+    fun userInteracting_transitioning_fromScene_userInputDriven() =
+        testScope.runTest() {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = SceneKey.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = key,
+                        toScene = SceneKey.Lockscreen,
+                        progress = progress,
+                        isUserInputDriven = true,
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is true
+            assertThat(interacting).isTrue()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is true
+            assertThat(interacting).isTrue()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is true
+            assertThat(interacting).isTrue()
+        }
+
+    @Test
+    fun userInteracting_transitioning_toAndFromDifferentScenes() =
+        testScope.runTest() {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to between different scenes
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = SceneKey.Lockscreen,
+                        toScene = SceneKey.QuickSettings,
+                        progress = progress,
+                        isUserInputDriven = true,
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index e086712..7463e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -151,6 +151,33 @@
         }
 
     @Test
+    fun updateLegacyShadeTracking() =
+        testScope.runTest {
+            assertThat(underTest.legacyShadeTracking.value).isEqualTo(false)
+
+            underTest.setLegacyShadeTracking(true)
+            assertThat(underTest.legacyShadeTracking.value).isEqualTo(true)
+        }
+
+    @Test
+    fun updateLegacyQsTracking() =
+        testScope.runTest {
+            assertThat(underTest.legacyQsTracking.value).isEqualTo(false)
+
+            underTest.setLegacyQsTracking(true)
+            assertThat(underTest.legacyQsTracking.value).isEqualTo(true)
+        }
+
+    @Test
+    fun updateLegacyExpandedOrAwaitingInputTransfer() =
+        testScope.runTest {
+            assertThat(underTest.legacyExpandedOrAwaitingInputTransfer.value).isEqualTo(false)
+
+            underTest.setLegacyExpandedOrAwaitingInputTransfer(true)
+            assertThat(underTest.legacyExpandedOrAwaitingInputTransfer.value).isEqualTo(true)
+        }
+
+    @Test
     fun updateUdfpsTransitionToFullShadeProgress() =
         testScope.runTest {
             assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt
index 8309342..36f82c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt
@@ -5,6 +5,7 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.google.common.truth.Expect
 import org.junit.Rule
 import org.junit.Test
@@ -23,7 +24,8 @@
             configurationController,
             context,
             splitShadeInterpolator,
-            portraitShadeInterpolator
+            portraitShadeInterpolator,
+            ResourcesSplitShadeStateController()
         )
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
index d5a1f80..7737b43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
@@ -9,6 +9,7 @@
 import com.android.systemui.shade.ShadeExpansionStateManager
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,6 +42,7 @@
                 context,
                 scrimShadeTransitionController,
                 statusBarStateController,
+                    ResourcesSplitShadeStateController()
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index a09e844..0925858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -83,7 +83,8 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.QuickSettings,
-                        progress = MutableStateFlow(0.5f)
+                        progress = MutableStateFlow(0.5f),
+                        isUserInputDriven = false,
                     )
                 )
             )
@@ -100,7 +101,8 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.QuickSettings,
                         toScene = SceneKey.Shade,
-                        progress = MutableStateFlow(0.5f)
+                        progress = MutableStateFlow(0.5f),
+                        isUserInputDriven = false,
                     )
                 )
             )
@@ -117,7 +119,8 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Gone,
                         toScene = SceneKey.Shade,
-                        progress = MutableStateFlow(0.5f)
+                        progress = MutableStateFlow(0.5f),
+                        isUserInputDriven = false,
                     )
                 )
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 88d853e..8f06e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -181,29 +181,30 @@
         String ACTION = "testAction";
     }
 
-    public void assertInstances(Integer allocated, Integer created) {
-        // Run the garbage collector to finalize and deallocate outstanding
-        // instances. Since the GC doesn't always appear to want to run
-        // completely when we ask, we ask it 10 times in a short loop.
-        for (int i = 0; i < 10; i++) {
+    private void assertInstances(int allocated, int created) {
+        // If there are more than the expected number of allocated instances, then we run the
+        // garbage collector to finalize and deallocate any outstanding non-referenced instances.
+        // Since the GC doesn't always appear to want to run completely when we ask, we do this up
+        // to 10 times before failing the test.
+        for (int i = 0; mCounter.getAllocatedInstances() > allocated && i < 10; i++) {
             System.runFinalization();
             System.gc();
         }
 
-        mCounter.assertInstances(allocated, created);
+        assertEquals(allocated, mCounter.getAllocatedInstances());
+        assertEquals(created, mCounter.getCreatedInstances());
     }
 
     public static class RefCounter {
         public final AtomicInteger mAllocatedInstances = new AtomicInteger();
         public final AtomicInteger mCreatedInstances = new AtomicInteger();
 
-        public void assertInstances(Integer allocated, Integer created) {
-            if (allocated != null) {
-                assertEquals(allocated.intValue(), mAllocatedInstances.get());
-            }
-            if (created != null) {
-                assertEquals(created.intValue(), mCreatedInstances.get());
-            }
+        public int getAllocatedInstances() {
+            return mAllocatedInstances.get();
+        }
+
+        public int getCreatedInstances() {
+            return mCreatedInstances.get();
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
index 7041262..673d63f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -59,7 +60,8 @@
                 context,
                 configurationController,
                 dumpManager,
-                qsProvider = { qS }
+                qsProvider = { qS },
+                ResourcesSplitShadeStateController()
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 2446234..2c9dfcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -110,6 +111,7 @@
     private val sharedNotificationContainerInteractor = SharedNotificationContainerInteractor(
         configurationRepository,
         mContext,
+            ResourcesSplitShadeStateController()
     )
     private val shadeInteractor =
         ShadeInteractor(
@@ -172,7 +174,8 @@
                         scrimController,
                         context,
                         configurationController,
-                        dumpManager
+                        dumpManager,
+                            ResourcesSplitShadeStateController()
                     ),
                 keyguardTransitionControllerFactory = { notificationPanelController ->
                     LockscreenShadeKeyguardTransitionController(
@@ -180,7 +183,8 @@
                         notificationPanelController,
                         context,
                         configurationController,
-                        dumpManager
+                        dumpManager,
+                            ResourcesSplitShadeStateController()
                     )
                 },
                 qsTransitionControllerFactory = { qsTransitionController },
@@ -188,6 +192,7 @@
                 shadeRepository = FakeShadeRepository(),
                 shadeInteractor = shadeInteractor,
                 powerInteractor = powerInteractor,
+                splitShadeStateController = ResourcesSplitShadeStateController()
             )
         transitionController.addCallback(transitionControllerCallback)
         whenever(nsslController.view).thenReturn(stackscroller)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index c49f179..a258f67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.WallpaperController
 import com.android.systemui.util.mockito.eq
@@ -114,8 +115,9 @@
                 notificationShadeWindowController,
                 dozeParameters,
                 context,
+                    ResourcesSplitShadeStateController(),
                 dumpManager,
-                configurationController)
+                configurationController,)
         notificationShadeDepthController.shadeAnimation = shadeAnimation
         notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
         notificationShadeDepthController.root = root
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
new file mode 100644
index 0000000..2c8900c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeStatusBarModeRepository : StatusBarModeRepository {
+    override val isTransientShown = MutableStateFlow(false)
+    override val isInFullscreenMode = MutableStateFlow(false)
+
+    override fun showTransient() {
+        isTransientShown.value = true
+    }
+    override fun clearTransient() {
+        isTransientShown.value = false
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
new file mode 100644
index 0000000..27c1ba3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -0,0 +1,257 @@
+/*
+ * 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.data.repository
+
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class StatusBarModeRepositoryImplTest : SysuiTestCase() {
+    private val testScope = TestScope()
+    private val commandQueue = mock<CommandQueue>()
+
+    private val underTest =
+        StatusBarModeRepositoryImpl(
+                testScope.backgroundScope,
+                DISPLAY_ID,
+                commandQueue,
+            )
+            .apply { this.start() }
+
+    private val commandQueueCallback: CommandQueue.Callbacks
+        get() {
+            val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+            verify(commandQueue).addCallback(callbackCaptor.capture())
+            return callbackCaptor.value
+        }
+
+    @Test
+    fun isTransientShown_commandQueueShow_wrongDisplayId_notUpdated() {
+        commandQueueCallback.showTransient(
+            DISPLAY_ID + 1,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+
+        assertThat(underTest.isTransientShown.value).isFalse()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueShow_notStatusBarType_notUpdated() {
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.navigationBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+
+        assertThat(underTest.isTransientShown.value).isFalse()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueShow_true() {
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+
+        assertThat(underTest.isTransientShown.value).isTrue()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueShow_statusBarAndOtherTypes_true() {
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars().or(WindowInsets.Type.navigationBars()),
+            /* isGestureOnSystemBar= */ false,
+        )
+
+        assertThat(underTest.isTransientShown.value).isTrue()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueAbort_wrongDisplayId_notUpdated() {
+        // Start as true
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+        assertThat(underTest.isTransientShown.value).isTrue()
+
+        // GIVEN the wrong display ID
+        commandQueueCallback.abortTransient(DISPLAY_ID + 1, WindowInsets.Type.statusBars())
+
+        // THEN the old value remains
+        assertThat(underTest.isTransientShown.value).isTrue()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueAbort_notStatusBarType_notUpdated() {
+        // Start as true
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+        assertThat(underTest.isTransientShown.value).isTrue()
+
+        // GIVEN the wrong type
+        commandQueueCallback.abortTransient(DISPLAY_ID, WindowInsets.Type.navigationBars())
+
+        // THEN the old value remains
+        assertThat(underTest.isTransientShown.value).isTrue()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueAbort_false() {
+        // Start as true
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+        assertThat(underTest.isTransientShown.value).isTrue()
+
+        commandQueueCallback.abortTransient(DISPLAY_ID, WindowInsets.Type.statusBars())
+
+        assertThat(underTest.isTransientShown.value).isFalse()
+    }
+
+    @Test
+    fun isTransientShown_commandQueueAbort_statusBarAndOtherTypes_false() {
+        // Start as true
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+        assertThat(underTest.isTransientShown.value).isTrue()
+
+        commandQueueCallback.abortTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars().or(WindowInsets.Type.captionBar()),
+        )
+
+        assertThat(underTest.isTransientShown.value).isFalse()
+    }
+
+    @Test
+    fun isTransientShown_showTransient_true() {
+        underTest.showTransient()
+
+        assertThat(underTest.isTransientShown.value).isTrue()
+    }
+
+    @Test
+    fun isTransientShown_clearTransient_false() {
+        // Start as true
+        commandQueueCallback.showTransient(
+            DISPLAY_ID,
+            WindowInsets.Type.statusBars(),
+            /* isGestureOnSystemBar= */ false,
+        )
+        assertThat(underTest.isTransientShown.value).isTrue()
+
+        underTest.clearTransient()
+
+        assertThat(underTest.isTransientShown.value).isFalse()
+    }
+
+    @Test
+    fun isInFullscreenMode_visibleTypesHasStatusBar_false() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isInFullscreenMode)
+
+            onSystemBarAttributesChanged(
+                requestedVisibleTypes = WindowInsets.Type.statusBars(),
+            )
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isInFullscreenMode_visibleTypesDoesNotHaveStatusBar_true() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isInFullscreenMode)
+
+            onSystemBarAttributesChanged(
+                requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+            )
+
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun isInFullscreenMode_wrongDisplayId_notUpdated() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isInFullscreenMode)
+
+            onSystemBarAttributesChanged(
+                requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+            )
+            assertThat(latest).isTrue()
+
+            onSystemBarAttributesChanged(
+                displayId = DISPLAY_ID + 1,
+                requestedVisibleTypes = WindowInsets.Type.statusBars(),
+            )
+
+            assertThat(latest).isTrue()
+        }
+
+    private fun onSystemBarAttributesChanged(
+        displayId: Int = DISPLAY_ID,
+        @WindowInsetsController.Appearance appearance: Int = APPEARANCE_OPAQUE_STATUS_BARS,
+        appearanceRegions: Array<AppearanceRegion> = emptyArray(),
+        navbarColorManagedByIme: Boolean = false,
+        @WindowInsetsController.Behavior behavior: Int = WindowInsetsController.BEHAVIOR_DEFAULT,
+        @WindowInsets.Type.InsetsType
+        requestedVisibleTypes: Int = WindowInsets.Type.defaultVisible(),
+        packageName: String = "package name",
+        letterboxDetails: Array<LetterboxDetails> = emptyArray(),
+    ) {
+        commandQueueCallback.onSystemBarAttributesChanged(
+            displayId,
+            appearance,
+            appearanceRegions,
+            navbarColorManagedByIme,
+            behavior,
+            requestedVisibleTypes,
+            packageName,
+            letterboxDetails,
+        )
+    }
+
+    private companion object {
+        const val DISPLAY_ID = 5
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
index 580463a..88ddc2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
@@ -56,6 +57,7 @@
         RemoteInputQuickSettingsDisabler(
             context,
             commandQueue,
+            ResourcesSplitShadeStateController(),
             configurationController,
         )
     private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
new file mode 100644
index 0000000..c650e01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
@@ -0,0 +1,359 @@
+/*
+ * 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.notification.row
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.NotificationDrawableConsumer
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+
+private const val FREE_IMAGE_DELAY_MS = 4000L
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BigPictureIconManagerTest : SysuiTestCase() {
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+    private val testableResources = context.orCreateTestableResources
+    private val imageLoader: ImageLoader = ImageLoader(context, testDispatcher)
+    private var mockConsumer: NotificationDrawableConsumer = mock()
+    private val drawableCaptor = argumentCaptor<Drawable>()
+
+    private lateinit var iconManager: BigPictureIconManager
+
+    private val expectedDrawable by lazy {
+        context.resources.getDrawable(R.drawable.dessert_zombiegingerbread, context.theme)
+    }
+    private val supportedIcon by lazy {
+        Icon.createWithContentUri(
+            Uri.parse(
+                "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+            )
+        )
+    }
+    private val unsupportedIcon by lazy {
+        Icon.createWithBitmap(
+            BitmapFactory.decodeResource(context.resources, R.drawable.dessert_zombiegingerbread)
+        )
+    }
+    private val invalidIcon by lazy { Icon.createWithContentUri(Uri.parse("this.is/broken")) }
+
+    @Before
+    fun setUp() {
+        iconManager =
+            BigPictureIconManager(
+                context,
+                imageLoader,
+                scope = testScope,
+                mainDispatcher = testDispatcher,
+                bgDispatcher = testDispatcher
+            )
+    }
+
+    @Test
+    fun onIconUpdated_supportedType_placeholderLoaded() =
+        testScope.runTest {
+            // WHEN update with a supported icon
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+            // THEN consumer is updated with a placeholder
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsPlaceHolder(drawableCaptor.value)
+            assertSize(drawableCaptor.value)
+        }
+
+    @Test
+    fun onIconUpdated_notSupportedType_fullImageLoaded() =
+        testScope.runTest {
+            // WHEN update with an unsupported icon
+            iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+
+            // THEN consumer is updated with the full image
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsFullImage(drawableCaptor.value)
+            assertSize(drawableCaptor.value)
+        }
+
+    @Test
+    fun onIconUpdated_invalidIcon_drawableIsNull() =
+        testScope.runTest {
+            // WHEN update with an invalid icon
+            iconManager.updateIcon(mockConsumer, invalidIcon).run()
+
+            // THEN consumer is updated with null
+            verify(mockConsumer).setImageDrawable(null)
+        }
+
+    @Test
+    fun onIconUpdated_consumerAlreadySet_nothingHappens() =
+        testScope.runTest {
+            // GIVEN a consumer is set
+            val otherConsumer: NotificationDrawableConsumer = mock()
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            reset(mockConsumer)
+
+            // WHEN a new consumer is set
+            iconManager.updateIcon(otherConsumer, unsupportedIcon).run()
+
+            // THEN nothing happens
+            verifyZeroInteractions(mockConsumer, otherConsumer)
+        }
+
+    @Test
+    fun onIconUpdated_iconAlreadySet_loadsNewIcon() =
+        testScope.runTest {
+            // GIVEN an icon is set
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            reset(mockConsumer)
+
+            // WHEN a new icon is set
+            iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+
+            // THEN consumer is updated with the new image
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsFullImage(drawableCaptor.value)
+            assertSize(drawableCaptor.value)
+        }
+
+    @Test
+    fun onIconUpdated_supportedTypeButTooWide_resizedPlaceholderLoaded() =
+        testScope.runTest {
+            // GIVEN the max width is smaller than our image
+            testableResources.addOverride(
+                com.android.internal.R.dimen.notification_big_picture_max_width,
+                20
+            )
+            iconManager.updateMaxImageSizes()
+
+            // WHEN update with a supported icon
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+            // THEN consumer is updated with the resized placeholder
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsPlaceHolder(drawableCaptor.value)
+            assertSize(drawableCaptor.value, expectedWidth = 20, expectedHeight = 20)
+        }
+
+    @Test
+    fun onIconUpdated_supportedTypeButTooHigh_resizedPlaceholderLoaded() =
+        testScope.runTest {
+            // GIVEN the max height is smaller than our image
+            testableResources.addOverride(
+                com.android.internal.R.dimen.notification_big_picture_max_height,
+                20
+            )
+            iconManager.updateMaxImageSizes()
+
+            // WHEN update with a supported icon
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+
+            // THEN consumer is updated with the resized placeholder
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsPlaceHolder(drawableCaptor.value)
+            assertSize(drawableCaptor.value, expectedWidth = 20, expectedHeight = 20)
+        }
+
+    @Test
+    fun onViewShown_placeholderShowing_fullImageLoaded() =
+        testScope.runTest {
+            // GIVEN placeholder is showing
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            reset(mockConsumer)
+
+            // WHEN the view is shown
+            iconManager.onViewShown(true)
+            runCurrent()
+
+            // THEN full image is set
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsFullImage(drawableCaptor.value)
+            assertSize(drawableCaptor.value)
+        }
+
+    @Test
+    fun onViewHidden_fullImageShowing_placeHolderSet() =
+        testScope.runTest {
+            // GIVEN full image is showing and the view is shown
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            iconManager.onViewShown(true)
+            runCurrent()
+            reset(mockConsumer)
+
+            // WHEN the view goes off the screen
+            iconManager.onViewShown(false)
+            // AND we wait a bit
+            advanceTimeBy(FREE_IMAGE_DELAY_MS)
+            runCurrent()
+
+            // THEN placeholder is set
+            verify(mockConsumer).setImageDrawable(drawableCaptor.capture())
+            assertIsPlaceHolder(drawableCaptor.value)
+            assertSize(drawableCaptor.value)
+        }
+
+    @Test
+    fun onViewShownToggled_viewShown_nothingHappens() =
+        testScope.runTest {
+            // GIVEN full image is showing and the view is shown
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            iconManager.onViewShown(true)
+            runCurrent()
+            reset(mockConsumer)
+
+            // WHEN the onViewShown is toggled
+            iconManager.onViewShown(false)
+            runCurrent()
+            iconManager.onViewShown(true)
+            // AND we wait a bit
+            advanceTimeBy(FREE_IMAGE_DELAY_MS)
+            runCurrent()
+
+            // THEN nothing happens
+            verifyZeroInteractions(mockConsumer)
+        }
+
+    // nice to have tests
+
+    @Test
+    fun onViewShown_fullImageLoaded_nothingHappens() =
+        testScope.runTest {
+            // GIVEN full image is showing
+            iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+            reset(mockConsumer)
+
+            // WHEN the view is shown
+            iconManager.onViewShown(true)
+            runCurrent()
+
+            // THEN nothing happens
+            verifyZeroInteractions(mockConsumer)
+        }
+
+    @Test
+    fun onViewHidden_placeholderShowing_nothingHappens() =
+        testScope.runTest {
+            // GIVEN placeholder image is showing
+            iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+            reset(mockConsumer)
+
+            // WHEN the view is hidden
+            iconManager.onViewShown(false)
+            // AND we wait a bit
+            advanceTimeBy(FREE_IMAGE_DELAY_MS)
+            runCurrent()
+
+            // THEN nothing happens
+            verifyZeroInteractions(mockConsumer)
+        }
+
+    @Test
+    fun onViewShown_alreadyShowing_nothingHappens() =
+        testScope.runTest {
+            // GIVEN full image is showing and the view is shown
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            iconManager.onViewShown(true)
+            runCurrent()
+            reset(mockConsumer)
+
+            // WHEN view shown called again
+            iconManager.onViewShown(true)
+            runCurrent()
+
+            verifyZeroInteractions(mockConsumer)
+        }
+
+    @Test
+    fun onViewHidden_alreadyHidden_nothingHappens() =
+        testScope.runTest {
+            // GIVEN placeholder image is showing and the view is hidden
+            iconManager.updateIcon(mockConsumer, unsupportedIcon).run()
+            iconManager.onViewShown(false)
+            advanceTimeBy(FREE_IMAGE_DELAY_MS)
+            runCurrent()
+            reset(mockConsumer)
+
+            // WHEN the view is hidden again
+            iconManager.onViewShown(false)
+            // AND we wait a bit
+            advanceTimeBy(FREE_IMAGE_DELAY_MS)
+            runCurrent()
+
+            // THEN nothing happens
+            verifyZeroInteractions(mockConsumer)
+        }
+
+    @Test
+    fun cancelJobs_freeImageJobRunning_jobCancelled() =
+        testScope.runTest {
+            // GIVEN full image is showing
+            iconManager.updateIcon(mockConsumer, supportedIcon).run()
+            iconManager.onViewShown(true)
+            runCurrent()
+            reset(mockConsumer)
+            // AND the view has just gone off the screen
+            iconManager.onViewShown(false)
+
+            // WHEN cancelJobs is called
+            iconManager.cancelJobs()
+            // AND we wait a bit
+            advanceTimeBy(FREE_IMAGE_DELAY_MS)
+            runCurrent()
+
+            // THEN no more updates are happening
+            verifyZeroInteractions(mockConsumer)
+        }
+
+    private fun assertIsPlaceHolder(drawable: Drawable) {
+        assertThat(drawable).isInstanceOf(PlaceHolderDrawable::class.java)
+    }
+
+    private fun assertIsFullImage(drawable: Drawable) {
+        assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
+    }
+
+    private fun assertSize(
+        drawable: Drawable,
+        expectedWidth: Int = expectedDrawable.intrinsicWidth,
+        expectedHeight: Int = expectedDrawable.intrinsicHeight
+    ) {
+        assertThat(drawable.intrinsicWidth).isEqualTo(expectedWidth)
+        assertThat(drawable.intrinsicHeight).isEqualTo(expectedHeight)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 79cf932..5606216 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -95,6 +95,7 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.settings.SecureSettings;
@@ -179,7 +180,7 @@
         allowTestableLooperAsMainThread();
         MockitoAnnotations.initMocks(this);
 
-        mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+        mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
 
         when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
         when(mKeyguardTransitionRepo.getTransitions()).thenReturn(emptyFlow());
@@ -713,7 +714,8 @@
                 mNotificationTargetsHelper,
                 mSecureSettings,
                 mock(NotificationDismissibilityProvider.class),
-                mActivityStarter
+                mActivityStarter,
+                new ResourcesSplitShadeStateController()
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 72fcdec..4307066 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -87,6 +87,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -322,26 +323,6 @@
     }
 
     @Test
-    public void setUnlockHintRunning_updatesStackEndHeightOnlyOnFinish() {
-        final float expansionFraction = 0.5f;
-        mAmbientState.setStatusBarState(StatusBarState.KEYGUARD);
-        mStackScroller.setUnlockHintRunning(true);
-
-        // Validate that when the animation is running, we update only the stackHeight
-        clearInvocations(mAmbientState);
-        mStackScroller.updateStackEndHeightAndStackHeight(expansionFraction);
-        verify(mAmbientState, never()).setStackEndHeight(anyFloat());
-        verify(mAmbientState).setStackHeight(anyFloat());
-
-        // Validate that when the animation ends the stackEndHeight is recalculated immediately
-        clearInvocations(mAmbientState);
-        mStackScroller.setUnlockHintRunning(false);
-        verify(mAmbientState).setUnlockHintRunning(eq(false));
-        verify(mAmbientState).setStackEndHeight(anyFloat());
-        verify(mAmbientState).setStackHeight(anyFloat());
-    }
-
-    @Test
     public void testNotDimmedOnKeyguard() {
         when(mBarState.getState()).thenReturn(StatusBarState.SHADE);
         mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
@@ -865,6 +846,7 @@
     public void testSplitShade_hasTopOverscroll() {
         mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
+        mStackScroller.passSplitShadeStateController(new ResourcesSplitShadeStateController());
         mStackScroller.updateSplitNotificationShade();
         mAmbientState.setExpansionFraction(1f);
 
@@ -880,6 +862,7 @@
     public void testNormalShade_hasNoTopOverscroll() {
         mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ false);
+        mStackScroller.passSplitShadeStateController(new ResourcesSplitShadeStateController());
         mStackScroller.updateSplitNotificationShade();
         mAmbientState.setExpansionFraction(1f);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 5279740..bc12bb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
@@ -70,7 +71,8 @@
                 statusBarStateController = sysuiStatusBarStateController,
                 lockscreenShadeTransitionController = lockscreenShadeTransitionController,
                 mediaDataManager = mediaDataManager,
-                testableResources.resources
+                testableResources.resources,
+                    ResourcesSplitShadeStateController()
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
index 7bbb094..7fc399b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -43,6 +44,7 @@
             SharedNotificationContainerInteractor(
                 configurationRepository,
                 mContext,
+                ResourcesSplitShadeStateController()
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 521069f..bfc0910 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
@@ -106,6 +107,7 @@
             SharedNotificationContainerInteractor(
                 configurationRepository,
                 mContext,
+                ResourcesSplitShadeStateController()
             )
         shadeInteractor =
             ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 3ad3c15..c71c0c57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -50,6 +50,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
 import com.android.systemui.shade.QuickSettingsController;
@@ -79,6 +80,7 @@
 public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
 
     @Mock private CentralSurfaces mCentralSurfaces;
+    @Mock private ScreenPinningRequest mScreenPinningRequest;
     @Mock private ShadeController mShadeController;
     @Mock private CommandQueue mCommandQueue;
     @Mock private QuickSettingsController mQuickSettingsController;
@@ -116,6 +118,7 @@
                 mQuickSettingsController,
                 mContext,
                 mContext.getResources(),
+                mScreenPinningRequest,
                 mShadeController,
                 mCommandQueue,
                 mShadeViewController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index bd3fb9f..26c0fd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -82,6 +82,7 @@
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.TestScopeProvider;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.InitController;
 import com.android.systemui.R;
@@ -116,7 +117,6 @@
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -146,6 +146,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -178,6 +179,7 @@
 import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.MessageRouterImpl;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -285,7 +287,6 @@
     @Mock private PluginManager mPluginManager;
     @Mock private ViewMediatorCallback mViewMediatorCallback;
     @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
-    @Mock private ScreenPinningRequest mScreenPinningRequest;
     @Mock private PluginDependencyProvider mPluginDependencyProvider;
     @Mock private ExtensionController mExtensionController;
     @Mock private UserInfoControllerImpl mUserInfoControllerImpl;
@@ -454,6 +455,7 @@
                         emptySet()),
                 mStatusBarWindowController,
                 mStatusBarWindowStateController,
+                new FakeStatusBarModeRepository(),
                 mKeyguardUpdateMonitor,
                 mStatusBarSignalPolicy,
                 mPulseExpansionHandler,
@@ -472,6 +474,7 @@
                 new DisplayMetrics(),
                 mMetricsLogger,
                 mShadeLogger,
+                new JavaAdapter(TestScopeProvider.getTestScope()),
                 mUiBgExecutor,
                 mNotificationPanelViewController,
                 mNotificationMediaManager,
@@ -508,7 +511,6 @@
                 mDozeServiceHost,
                 mBackActionInteractor,
                 mPowerManager,
-                mScreenPinningRequest,
                 mDozeScrimController,
                 mVolumeComponent,
                 mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index d100c687..79f8dbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -30,6 +30,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -51,6 +53,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.statusbar.CommandQueue;
@@ -79,6 +83,7 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
+    private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
     @Mock
     private CarrierTextController mCarrierTextController;
     @Mock
@@ -131,6 +136,7 @@
 
     @Before
     public void setup() throws Exception {
+        mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
         mShadeViewStateProvider = new TestShadeViewStateProvider();
 
         MockitoAnnotations.initMocks(this);
@@ -166,6 +172,7 @@
                 mBiometricUnlockController,
                 mStatusBarStateController,
                 mStatusBarContentInsetsProvider,
+                mFeatureFlags,
                 mUserManager,
                 mStatusBarUserChipViewModel,
                 mSecureSettings,
@@ -228,6 +235,30 @@
     }
 
     @Test
+    public void onViewReAttached_flagOff_iconManagerNotReRegistered() {
+        mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false);
+        mController.onViewAttached();
+        mController.onViewDetached();
+        reset(mStatusBarIconController);
+
+        mController.onViewAttached();
+
+        verify(mStatusBarIconController, never()).addIconGroup(any());
+    }
+
+    @Test
+    public void onViewReAttached_flagOn_iconManagerReRegistered() {
+        mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true);
+        mController.onViewAttached();
+        mController.onViewDetached();
+        reset(mStatusBarIconController);
+
+        mController.onViewAttached();
+
+        verify(mStatusBarIconController).addIconGroup(any());
+    }
+    
+    @Test
     public void setBatteryListening_true_callbackAdded() {
         mController.setBatteryListening(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 798c3f9..bac8579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -268,7 +268,6 @@
 
     @Test
     public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
-        when(mShadeViewController.isUnlockHintRunning()).thenReturn(true);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
         verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
     }
@@ -977,4 +976,23 @@
         verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
         verify(mKeyguardMessageAreaController).setMessage(eq(""));
     }
+
+    @Test
+    public void testShowBouncerOrKeyguard_needsFullScreen() {
+        when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+                KeyguardSecurityModel.SecurityMode.SimPin);
+        mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+        verify(mCentralSurfaces).hideKeyguard();
+        verify(mPrimaryBouncerInteractor).show(true);
+    }
+
+    @Test
+    public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
+        when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+                KeyguardSecurityModel.SecurityMode.SimPin);
+        when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+        mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+        verify(mCentralSurfaces, never()).hideKeyguard();
+        verify(mPrimaryBouncerInteractor, never()).show(true);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
index 4d79a53..0cd2214 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
@@ -11,11 +11,9 @@
 import com.android.internal.view.AppearanceRegion
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.SysuiStatusBarStateController
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
@@ -33,7 +31,6 @@
 
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var lightBarController: LightBarController
-    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
     @Mock private lateinit var letterboxAppearanceCalculator: LetterboxAppearanceCalculator
     @Mock private lateinit var centralSurfaces: CentralSurfaces
 
@@ -52,7 +49,6 @@
             SystemBarAttributesListener(
                 centralSurfaces,
                 letterboxAppearanceCalculator,
-                statusBarStateController,
                 lightBarController,
                 dumpManager)
     }
@@ -81,23 +77,6 @@
     }
 
     @Test
-    fun onSysBarAttrsChanged_forwardsAppearanceToStatusBarStateController() {
-        changeSysBarAttrs(TEST_APPEARANCE)
-
-        verify(statusBarStateController)
-            .setSystemBarAttributes(eq(TEST_APPEARANCE), anyInt(), anyInt(), any())
-    }
-
-    @Test
-    fun onSysBarAttrsChanged_forwardsLetterboxAppearanceToStatusBarStateCtrl() {
-        changeSysBarAttrs(TEST_APPEARANCE, TEST_LETTERBOX_DETAILS)
-
-        verify(statusBarStateController)
-            .setSystemBarAttributes(
-                eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), anyInt(), any())
-    }
-
-    @Test
     fun onSysBarAttrsChanged_forwardsAppearanceToLightBarController() {
         changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS)
 
@@ -119,21 +98,9 @@
     }
 
     @Test
-    fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToStatusBarStateController() {
-        changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
-        reset(centralSurfaces, lightBarController, statusBarStateController)
-
-        sysBarAttrsListener.onStatusBarBoundsChanged()
-
-        verify(statusBarStateController)
-            .setSystemBarAttributes(
-                eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), anyInt(), any())
-    }
-
-    @Test
     fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToLightBarController() {
         changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
-        reset(centralSurfaces, lightBarController, statusBarStateController)
+        reset(centralSurfaces, lightBarController)
 
         sysBarAttrsListener.onStatusBarBoundsChanged()
 
@@ -148,7 +115,7 @@
     @Test
     fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToCentralSurfaces() {
         changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
-        reset(centralSurfaces, lightBarController, statusBarStateController)
+        reset(centralSurfaces, lightBarController)
 
         sysBarAttrsListener.onStatusBarBoundsChanged()
 
@@ -158,11 +125,11 @@
     @Test
     fun onStatusBarBoundsChanged_previousCallEmptyLetterbox_doesNothing() {
         changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf())
-        reset(centralSurfaces, lightBarController, statusBarStateController)
+        reset(centralSurfaces, lightBarController)
 
         sysBarAttrsListener.onStatusBarBoundsChanged()
 
-        verifyZeroInteractions(centralSurfaces, lightBarController, statusBarStateController)
+        verifyZeroInteractions(centralSurfaces, lightBarController)
     }
 
     private fun changeSysBarAttrs(@Appearance appearance: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 711e4ac..dc5d55e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -34,17 +34,20 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -62,7 +65,6 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.util.*
 
 private const val CALL_UID = 900
 
@@ -75,23 +77,24 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
 class OngoingCallControllerTest : SysuiTestCase() {
 
     private val clock = FakeSystemClock()
     private val mainExecutor = FakeExecutor(clock)
     private val uiEventLoggerFake = UiEventLoggerFake()
+    private val testScope = TestScope()
+    private val statusBarModeRepository = FakeStatusBarModeRepository()
 
     private lateinit var controller: OngoingCallController
     private lateinit var notifCollectionListener: NotifCollectionListener
 
-    @Mock private lateinit var mockOngoingCallFlags: OngoingCallFlags
     @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
         SwipeStatusBarAwayGestureHandler
     @Mock private lateinit var mockOngoingCallListener: OngoingCallListener
     @Mock private lateinit var mockActivityStarter: ActivityStarter
     @Mock private lateinit var mockIActivityManager: IActivityManager
     @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
-    @Mock private lateinit var mockStatusBarStateController: StatusBarStateController
 
     private lateinit var chipView: View
 
@@ -103,24 +106,23 @@
         }
 
         MockitoAnnotations.initMocks(this)
-        `when`(mockOngoingCallFlags.isStatusBarChipEnabled()).thenReturn(true)
         val notificationCollection = mock(CommonNotifCollection::class.java)
 
         controller = OngoingCallController(
-                context,
-                notificationCollection,
-                mockOngoingCallFlags,
-                clock,
-                mockActivityStarter,
-                mainExecutor,
-                mockIActivityManager,
-                OngoingCallLogger(uiEventLoggerFake),
-                DumpManager(),
-                Optional.of(mockStatusBarWindowController),
-                Optional.of(mockSwipeStatusBarAwayGestureHandler),
-                mockStatusBarStateController,
-            )
-        controller.init()
+            testScope.backgroundScope,
+            context,
+            notificationCollection,
+            clock,
+            mockActivityStarter,
+            mainExecutor,
+            mockIActivityManager,
+            OngoingCallLogger(uiEventLoggerFake),
+            DumpManager(),
+            mockStatusBarWindowController,
+            mockSwipeStatusBarAwayGestureHandler,
+            statusBarModeRepository,
+        )
+        controller.start()
         controller.addCallback(mockOngoingCallListener)
         controller.setChipView(chipView)
 
@@ -494,44 +496,10 @@
     }
 
     @Test
-    fun fullscreenIsTrue_thenCallNotificationAdded_chipNotClickable() {
-        `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
-        getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
+    fun fullscreenIsTrue_chipStillClickable() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
-        assertThat(chipView.hasOnClickListeners()).isFalse()
-    }
-
-    @Test
-    fun callNotificationAdded_thenFullscreenIsTrue_chipNotClickable() {
-        `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
-        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-        getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
-
-        assertThat(chipView.hasOnClickListeners()).isFalse()
-    }
-
-    @Test
-    fun fullscreenChangesToFalse_chipClickable() {
-        `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(false)
-
-        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-        // First, update to true
-        getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
-        // Then, update to false
-        getStateListener().onFullscreenStateChanged(/* isFullscreen= */ false)
-
-        assertThat(chipView.hasOnClickListeners()).isTrue()
-    }
-
-    @Test
-    fun fullscreenIsTrue_butChipClickInImmersiveEnabled_chipClickable() {
-        `when`(mockOngoingCallFlags.isInImmersiveChipTapEnabled()).thenReturn(true)
-
-        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-        getStateListener().onFullscreenStateChanged(/* isFullscreen= */ true)
+        statusBarModeRepository.isInFullscreenMode.value = true
+        testScope.runCurrent()
 
         assertThat(chipView.hasOnClickListeners()).isTrue()
     }
@@ -540,7 +508,8 @@
 
     @Test
     fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
-        getStateListener().onFullscreenStateChanged(true)
+        statusBarModeRepository.isInFullscreenMode.value = true
+        testScope.runCurrent()
 
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
@@ -550,7 +519,8 @@
 
     @Test
     fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
-        getStateListener().onFullscreenStateChanged(false)
+        statusBarModeRepository.isInFullscreenMode.value = false
+        testScope.runCurrent()
 
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
@@ -562,7 +532,8 @@
     fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
-        getStateListener().onFullscreenStateChanged(true)
+        statusBarModeRepository.isInFullscreenMode.value = true
+        testScope.runCurrent()
 
         verify(mockSwipeStatusBarAwayGestureHandler)
             .addOnGestureDetectedCallback(anyString(), any())
@@ -570,11 +541,12 @@
 
     @Test
     fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
-        getStateListener().onFullscreenStateChanged(true)
+        statusBarModeRepository.isInFullscreenMode.value = true
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         reset(mockSwipeStatusBarAwayGestureHandler)
 
-        getStateListener().onFullscreenStateChanged(false)
+        statusBarModeRepository.isInFullscreenMode.value = false
+        testScope.runCurrent()
 
         verify(mockSwipeStatusBarAwayGestureHandler)
             .removeOnGestureDetectedCallback(anyString())
@@ -582,7 +554,8 @@
 
     @Test
     fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
-        getStateListener().onFullscreenStateChanged(true)
+        statusBarModeRepository.isInFullscreenMode.value = true
+        testScope.runCurrent()
         val ongoingCallNotifEntry = createOngoingCallNotifEntry()
         notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
         reset(mockSwipeStatusBarAwayGestureHandler)
@@ -623,13 +596,6 @@
     }
 
     private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
-
-    private fun getStateListener(): StatusBarStateController.StateListener {
-        val statusBarStateListenerCaptor = ArgumentCaptor.forClass(
-            StatusBarStateController.StateListener::class.java)
-        verify(mockStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture())
-        return statusBarStateListenerCaptor.value!!
-    }
 }
 
 private val person = Person.Builder().setName("name").build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
index 1ab0582..cfb48a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
@@ -49,7 +49,9 @@
 
         remoteInputQuickSettingsDisabler = RemoteInputQuickSettingsDisabler(
             mContext,
-            commandQueue, Mockito.mock(ConfigurationController::class.java)
+            commandQueue,
+                ResourcesSplitShadeStateController(),
+            Mockito.mock(ConfigurationController::class.java),
         )
         configuration = Configuration(mContext.resources.configuration)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 7f3d4b7..66c5aaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -132,12 +132,6 @@
     }
 
     @Test
-    public void testAddNullCallback() {
-        mController.addCallback(null);
-        mController.fireConfigChanged(null);
-    }
-
-    @Test
     public void testModeChange() {
         List<Integer> states = List.of(
                 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 3152fd1..aa9d62da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,6 +41,7 @@
 import android.app.KeyguardManager;
 import android.content.res.Configuration;
 import android.media.AudioManager;
+import android.os.Handler;
 import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -146,6 +147,10 @@
         mTestableLooper = TestableLooper.get(this);
         allowTestableLooperAsMainThread();
 
+        // Ensure previous tests have not left messages on main looper
+        Handler localHandler = new Handler(mTestableLooper.getLooper());
+        localHandler.removeCallbacksAndMessages(null);
+
         when(mPostureController.getDevicePosture())
                 .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 692af6a..c1d11aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -459,7 +459,7 @@
                         WalletCard.CARD_TYPE_UNKNOWN),
                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_PAYMENT),
                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_NON_PAYMENT)
-                );
+        );
         GetWalletCardsResponse response = new GetWalletCardsResponse(walletCardList, 0);
         mController.onWalletCardsRetrieved(response);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
new file mode 100644
index 0000000..e46c1f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Test class for WalletCardUtils */
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class WalletCardUtilsTest : SysuiTestCase() {
+
+    private val paymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_PAYMENT)
+    private val nonPaymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_NON_PAYMENT)
+    private val unknownCard = createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+
+    @Test
+    fun paymentCards_cardTypesAllUnknown_getsAllCards() {
+        val walletCardList =
+            mutableListOf(
+                createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+                createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+                createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+            )
+
+        assertThat(walletCardList).isEqualTo(getPaymentCards(walletCardList))
+    }
+
+    @Test
+    fun paymentCards_cardTypesDifferent_onlyGetsPayment() {
+        val walletCardList = mutableListOf(paymentCard, nonPaymentCard, unknownCard)
+
+        assertThat(getPaymentCards(walletCardList)).isEqualTo(mutableListOf(paymentCard))
+    }
+
+    private fun createWalletCardWithType(cardType: Int): WalletCard {
+        return WalletCard.Builder(
+                /*cardId= */ CARD_ID,
+                /*cardType= */ cardType,
+                /*cardImage= */ mock(),
+                /*contentDescription=  */ CARD_DESCRIPTION,
+                /*pendingIntent= */ mock()
+            )
+            .build()
+    }
+
+    companion object {
+        private const val CARD_ID: String = "ID"
+        private const val CARD_DESCRIPTION: String = "Description"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index ef0adbb..d2c8aea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -41,6 +41,7 @@
 import com.android.wm.shell.onehanded.OneHandedEventCallback;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.sysui.ShellInterface;
 
@@ -79,6 +80,7 @@
     @Mock ShellExecutor mSysUiMainExecutor;
     @Mock NoteTaskInitializer mNoteTaskInitializer;
     @Mock DesktopMode mDesktopMode;
+    @Mock RecentTasks mRecentTasks;
 
     @Before
     public void setUp() {
@@ -91,6 +93,7 @@
                 Optional.of(mSplitScreen),
                 Optional.of(mOneHanded),
                 Optional.of(mDesktopMode),
+                Optional.of(mRecentTasks),
                 mCommandQueue,
                 mConfigurationController,
                 mKeyguardStateController,
@@ -129,4 +132,10 @@
                 any(DesktopModeTaskRepository.VisibleTasksListener.class),
                 any(Executor.class));
     }
+
+    @Test
+    public void initRecentTasks_registersListener() {
+        mWMShell.initRecentTasks(mRecentTasks);
+        verify(mRecentTasks).addAnimationStateListener(any(Executor.class), any());
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
similarity index 71%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
index fd91391..60291ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
@@ -17,15 +17,23 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
-class FakeRearDisplayStateRepository : RearDisplayStateRepository {
+class FakeDisplayStateRepository : DisplayStateRepository {
     private val _isInRearDisplayMode = MutableStateFlow<Boolean>(false)
     override val isInRearDisplayMode: StateFlow<Boolean> = _isInRearDisplayMode.asStateFlow()
 
+    private val _currentRotation = MutableStateFlow<DisplayRotation>(DisplayRotation.ROTATION_0)
+    override val currentRotation: StateFlow<DisplayRotation> = _currentRotation.asStateFlow()
+
     fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
         _isInRearDisplayMode.value = isInRearDisplayMode
     }
+
+    fun setCurrentRotation(currentRotation: DisplayRotation) {
+        _currentRotation.value = currentRotation
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
new file mode 100644
index 0000000..e1c6dde
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.communal.data.repository
+
+/** Fake implementation of [CommunalRepository]. */
+class FakeCommunalRepository : CommunalRepository {
+    override var isCommunalEnabled = false
+
+    fun setIsCommunalEnabled(value: Boolean) {
+        isCommunalEnabled = value
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2b13dca..322fb28 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -56,20 +56,7 @@
         _isLockedOut.value = isLockedOut
     }
 
-    private val faceAuthPaused = MutableStateFlow(false)
-    override fun pauseFaceAuth() {
-        faceAuthPaused.value = true
-    }
-
-    override fun resumeFaceAuth() {
-        faceAuthPaused.value = false
-    }
-
-    fun isFaceAuthPaused(): Boolean {
-        return faceAuthPaused.value
-    }
-
-    override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+    override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
         _runningAuthRequest.value = uiEvent to fallbackToDetection
         _isAuthRunning.value = true
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
index 9383a0a..bf26e71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
@@ -16,21 +16,11 @@
 
 package com.android.systemui.qs
 
-import android.content.Context
 import com.android.systemui.plugins.qs.QSFactory
 import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.QSTileView
 
 class FakeQSFactory(private val tileCreator: (String) -> QSTile?) : QSFactory {
     override fun createTile(tileSpec: String): QSTile? {
         return tileCreator(tileSpec)
     }
-
-    override fun createTileView(
-        context: Context?,
-        tile: QSTile?,
-        collapsedView: Boolean
-    ): QSTileView {
-        throw NotImplementedError("Not implemented")
-    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
new file mode 100644
index 0000000..13437c9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -0,0 +1,64 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import javax.annotation.CheckReturnValue
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+class FakeQSTileDataInteractor<T>(
+    private val dataFlow: MutableSharedFlow<FakeData<T>> =
+        MutableSharedFlow(replay = Int.MAX_VALUE),
+    private val availabilityFlow: MutableSharedFlow<Boolean> =
+        MutableSharedFlow(replay = Int.MAX_VALUE),
+) : QSTileDataInteractor<T> {
+
+    private val mutableDataRequests = mutableListOf<QSTileDataRequest>()
+    val dataRequests: List<QSTileDataRequest> = mutableDataRequests
+
+    private val mutableAvailabilityRequests = mutableListOf<Unit>()
+    val availabilityRequests: List<Unit> = mutableAvailabilityRequests
+
+    @CheckReturnValue
+    fun emitData(data: T): FilterEmit =
+        object : FilterEmit {
+            override fun forRequest(request: QSTileDataRequest): Boolean =
+                dataFlow.tryEmit(FakeData(data, DataFilter.ForRequest(request)))
+            override fun forAnyRequest(): Boolean = dataFlow.tryEmit(FakeData(data, DataFilter.Any))
+        }
+
+    fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
+    suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
+
+    override fun tileData(qsTileDataRequest: QSTileDataRequest): Flow<T> {
+        mutableDataRequests.add(qsTileDataRequest)
+        return dataFlow
+            .filter {
+                when (it.filter) {
+                    is DataFilter.Any -> true
+                    is DataFilter.ForRequest -> it.filter.request == qsTileDataRequest
+                }
+            }
+            .map { it.data }
+    }
+
+    override fun availability(): Flow<Boolean> {
+        mutableAvailabilityRequests.add(Unit)
+        return availabilityFlow
+    }
+
+    interface FilterEmit {
+        fun forRequest(request: QSTileDataRequest): Boolean
+        fun forAnyRequest(): Boolean
+    }
+
+    class FakeData<T>(
+        val data: T,
+        val filter: DataFilter,
+    )
+
+    sealed class DataFilter {
+        object Any : DataFilter()
+        class ForRequest(val request: QSTileDataRequest) : DataFilter()
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
new file mode 100644
index 0000000..4e0266e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.qs.tiles.base.interactor
+
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+class FakeQSTileUserActionInteractor<T> : QSTileUserActionInteractor<T> {
+
+    private val mutex: Mutex = Mutex()
+    private val mutableInputs: MutableList<FakeInput<T>> = mutableListOf()
+
+    val inputs: List<FakeInput<T>> = mutableInputs
+
+    fun lastInput(): FakeInput<T>? = inputs.lastOrNull()
+
+    override suspend fun handleInput(userAction: QSTileUserAction, currentData: T) {
+        mutex.withLock { mutableInputs.add(FakeInput(userAction, currentData)) }
+    }
+
+    data class FakeInput<T>(val userAction: QSTileUserAction, val data: T)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 2d79e0f..1620dc27 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -31,6 +31,9 @@
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
@@ -94,7 +97,10 @@
             )
         }
     }
-
+    val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
+    private val communalWidgetRepository: FakeCommunalWidgetRepository by lazy {
+        FakeCommunalWidgetRepository()
+    }
     val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
 
     private val context = test.context
@@ -171,6 +177,13 @@
         )
     }
 
+    fun communalInteractor(): CommunalInteractor {
+        return CommunalInteractor(
+            communalRepository = communalRepository,
+            widgetRepository = communalWidgetRepository,
+        )
+    }
+
     fun bouncerInteractor(
         authenticationInteractor: AuthenticationInteractor,
         sceneInteractor: SceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 4242c16..f5f924d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -22,7 +22,6 @@
 import android.os.UserHandle
 import android.test.mock.MockContentResolver
 import com.android.systemui.util.mockito.mock
-import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 
 /** A fake [UserTracker] to be used in tests. */
@@ -73,8 +72,7 @@
 
     fun onUserChanging(userId: Int = _userId) {
         val copy = callbacks.toList()
-        val latch = CountDownLatch(copy.size)
-        copy.forEach { it.onUserChanging(userId, userContext, latch) }
+        copy.forEach { it.onUserChanging(userId, userContext) {} }
     }
 
     fun onUserChanged(userId: Int = _userId) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 08152a3..8b721b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -40,6 +40,34 @@
     @Deprecated("Use ShadeInteractor instead")
     override val legacyShadeExpansion = _legacyShadeExpansion
 
+    private val _legacyShadeTracking = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead")
+    override val legacyShadeTracking = _legacyShadeTracking
+
+    private val _legacyQsTracking = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead") override val legacyQsTracking = _legacyQsTracking
+
+    private val _legacyExpandedOrAwaitingInputTransfer = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead")
+    override val legacyExpandedOrAwaitingInputTransfer = _legacyExpandedOrAwaitingInputTransfer
+
+    @Deprecated("Use ShadeInteractor instead")
+    override fun setLegacyExpandedOrAwaitingInputTransfer(
+        legacyExpandedOrAwaitingInputTransfer: Boolean
+    ) {
+        _legacyExpandedOrAwaitingInputTransfer.value = legacyExpandedOrAwaitingInputTransfer
+    }
+
+    @Deprecated("Should only be called by NPVC and tests")
+    override fun setLegacyQsTracking(legacyQsTracking: Boolean) {
+        _legacyQsTracking.value = legacyQsTracking
+    }
+
+    @Deprecated("Should only be called by NPVC and tests")
+    override fun setLegacyShadeTracking(tracking: Boolean) {
+        _legacyShadeTracking.value = tracking
+    }
+
     fun setShadeModel(model: ShadeModel) {
         _shadeModel.value = model
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 05b6eb4..44ffb51 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -56,7 +56,6 @@
 import android.graphics.Region;
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -98,7 +97,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
 import com.android.server.accessibility.magnification.MagnificationProcessor;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -1441,19 +1439,24 @@
                     AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
             return;
         }
-
         final long identity = Binder.clearCallingIdentity();
         try {
-            mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
-                final ScreenshotHardwareBuffer screenshotBuffer = LocalServices
-                        .getService(DisplayManagerInternal.class).userScreenshot(displayId);
-                if (screenshotBuffer != null) {
-                    sendScreenshotSuccess(screenshotBuffer, callback);
-                } else {
-                    sendScreenshotFailure(
-                            AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
-                }
-            }, null).recycleOnUse());
+            ScreenCapture.ScreenCaptureListener screenCaptureListener = new
+                    ScreenCapture.ScreenCaptureListener(
+                    (screenshotBuffer, result) -> {
+                        if (screenshotBuffer != null && result == 0) {
+                            sendScreenshotSuccess(screenshotBuffer, callback);
+                        } else {
+                            sendScreenshotFailure(
+                                    AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+                                    callback);
+                        }
+                    }
+            );
+            mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
+        } catch (Exception e) {
+            sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+                    callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1461,22 +1464,24 @@
 
     private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer,
             RemoteCallback callback) {
-        final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
-        final ParcelableColorSpace colorSpace =
-                new ParcelableColorSpace(screenshotBuffer.getColorSpace());
+        mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+            final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+            final ParcelableColorSpace colorSpace =
+                    new ParcelableColorSpace(screenshotBuffer.getColorSpace());
 
-        final Bundle payload = new Bundle();
-        payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
-                AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
-        payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
-                hardwareBuffer);
-        payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
-        payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
-                SystemClock.uptimeMillis());
+            final Bundle payload = new Bundle();
+            payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
+                    AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
+            payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+                    hardwareBuffer);
+            payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+            payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
+                    SystemClock.uptimeMillis());
 
-        // Send back the result.
-        callback.sendResult(payload);
-        hardwareBuffer.close();
+            // Send back the result.
+            callback.sendResult(payload);
+            hardwareBuffer.close();
+        }, null).recycleOnUse());
     }
 
     private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 1e44de6..92e00ee 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -1 +1,8 @@
-package: "android.service.autofill"
\ No newline at end of file
+package: "android.service.autofill"
+
+flag {
+  name: "autofill_credman_integration"
+  namespace: "autofill"
+  description: "Guards Autofill Framework against Autofill-Credman integration"
+  bug: "296907283"
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 330e35b..ba45339 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -887,6 +887,11 @@
 
         @Override
         public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+            // TODO: temporary fix, will remove soon
+            AssociationInfo association = mAssociationStore.getAssociationById(associationId);
+            if (association == null) {
+                return null;
+            }
             getAssociationWithCallerChecks(associationId);
             return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
         }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 42b5a8b..e5c847a 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -139,7 +139,7 @@
             @UserIdInt int userId, int associationId) {
         if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) {
             Slog.i(LOG_TAG, "User consent Intent should be skipped. Returning null.");
-            // Auto enable perm sync for the whitelisted packages, but don't override user decision
+            // Auto enable perm sync for the allowlisted packages, but don't override user decision
             PermissionSyncRequest request = getPermissionSyncRequest(associationId);
             if (request == null) {
                 PermissionSyncRequest newRequest = new PermissionSyncRequest(associationId);
@@ -224,20 +224,30 @@
      * Enable perm sync for the association
      */
     public void enablePermissionsSync(int associationId) {
-        int userId = mAssociationStore.getAssociationById(associationId).getUserId();
-        PermissionSyncRequest request = new PermissionSyncRequest(associationId);
-        request.setUserConsented(true);
-        mSystemDataTransferRequestStore.writeRequest(userId, request);
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+            request.setUserConsented(true);
+            mSystemDataTransferRequestStore.writeRequest(userId, request);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
     }
 
     /**
      * Disable perm sync for the association
      */
     public void disablePermissionsSync(int associationId) {
-        int userId = mAssociationStore.getAssociationById(associationId).getUserId();
-        PermissionSyncRequest request = new PermissionSyncRequest(associationId);
-        request.setUserConsented(false);
-        mSystemDataTransferRequestStore.writeRequest(userId, request);
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+            request.setUserConsented(false);
+            mSystemDataTransferRequestStore.writeRequest(userId, request);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
     }
 
     /**
@@ -245,23 +255,34 @@
      */
     @Nullable
     public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
-        int userId = mAssociationStore.getAssociationById(associationId).getUserId();
-        List<SystemDataTransferRequest> requests =
-                mSystemDataTransferRequestStore.readRequestsByAssociationId(userId, associationId);
-        for (SystemDataTransferRequest request : requests) {
-            if (request instanceof PermissionSyncRequest) {
-                return (PermissionSyncRequest) request;
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            List<SystemDataTransferRequest> requests =
+                    mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
+                            associationId);
+            for (SystemDataTransferRequest request : requests) {
+                if (request instanceof PermissionSyncRequest) {
+                    return (PermissionSyncRequest) request;
+                }
             }
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
         }
-        return null;
     }
 
     /**
      * Remove perm sync request for the association.
      */
     public void removePermissionSyncRequest(int associationId) {
-        int userId = mAssociationStore.getAssociationById(associationId).getUserId();
-        mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
     }
 
     private void onReceivePermissionRestore(byte[] message) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ee18743..d9c2694 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -180,7 +180,6 @@
         "android.hardware.rebootescrow-V1-java",
         "android.hardware.power.stats-V2-java",
         "android.hidl.manager-V1.2-java",
-        "android.security.flags-aconfig-java",
         "cbor-java",
         "display_flags_lib",
         "icu4j_calendar_astronomer",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 838aae8..cd87908 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -112,7 +112,14 @@
      */
     public static final int INTEGRITY_VERIFICATION_REJECT = 0;
 
-    /** Observer called whenever the list of packages changes */
+    /**
+     * Observer called whenever the list of packages changes.
+     *
+     * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+     * PackageMonitor covers more installation and uninstallation corner cases than
+     * PackageListObserver.
+     */
+    @Deprecated
     public interface PackageListObserver {
         /** A package was added to the system. */
         default void onPackageAdded(@NonNull String packageName, int uid) {}
@@ -723,7 +730,12 @@
      * notified if a package is updated.
      * <p>The package list will not be updated automatically as packages are
      * installed / uninstalled. Any changes must be handled within the observer.
+     *
+     * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+     * PackageMonitor covers more installation and uninstallation corner cases than
+     * PackageListObserver.
      */
+    @Deprecated
     public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
 
     /**
@@ -733,7 +745,12 @@
      * <p>Does nothing if the observer isn't currently registered.
      * <p>Observers are notified asynchronously and it's possible for an observer to be
      * invoked after its been removed.
+     *
+     * @deprecated please use {@link com.android.internal.content.PackageMonitor} instead.
+     * PackageMonitor covers more installation and uninstallation corner cases than
+     * PackageListObserver.
      */
+    @Deprecated
     public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
 
     /**
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
index 8996926..44f1e76 100644
--- a/services/core/java/com/android/server/SmartStorageMaintIdler.java
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 public class SmartStorageMaintIdler extends JobService {
     private static final String TAG = "SmartStorageMaintIdler";
@@ -34,15 +35,15 @@
 
     private static final int SMART_MAINT_JOB_ID = 2808;
 
-    private boolean mStarted;
+    private final AtomicBoolean mStarted = new AtomicBoolean(false);
     private JobParameters mJobParams;
     private final Runnable mFinishCallback = new Runnable() {
         @Override
         public void run() {
             Slog.i(TAG, "Got smart storage maintenance service completion callback");
-            if (mStarted) {
+            if (mStarted.get()) {
                 jobFinished(mJobParams, false);
-                mStarted = false;
+                mStarted.set(false);
             }
             // ... and try again in a next period
             scheduleSmartIdlePass(SmartStorageMaintIdler.this,
@@ -52,18 +53,26 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
-        mJobParams = params;
-        StorageManagerService ms = StorageManagerService.sSelf;
-        if (ms != null) {
-            mStarted = true;
-            ms.runSmartIdleMaint(mFinishCallback);
+        final StorageManagerService ms = StorageManagerService.sSelf;
+        if (mStarted.compareAndSet(false, true)) {
+            new Thread() {
+                public void run() {
+                    mJobParams = params;
+                    if (ms != null) {
+                        ms.runSmartIdleMaint(mFinishCallback);
+                    } else {
+                        mStarted.set(false);
+                    }
+                }
+            }.start();
+            return ms != null;
         }
-        return ms != null;
+        return false;
     }
 
     @Override
     public boolean onStopJob(JobParameters params) {
-        mStarted = false;
+        mStarted.set(false);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a787644..25ca509c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2842,7 +2842,7 @@
         return true;
     }
 
-    void runSmartIdleMaint(Runnable callback) {
+    synchronized void runSmartIdleMaint(Runnable callback) {
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
 
         try {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 93fe0c9..553b085 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -97,6 +97,11 @@
 import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
 
 import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_BG_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_DELEGATE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NONE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_FOREGROUND_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_SERVICE;
 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED;
 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER;
 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT;
@@ -122,6 +127,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.Manifest;
+import android.Manifest.permission;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -901,7 +907,10 @@
                 showFgsBgRestrictedNotificationLocked(r);
                 logFGSStateChangeLocked(r,
                         FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
-                        0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+                        0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                        FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                        false /* fgsRestrictionRecalculated */
+                );
                 if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) {
                     throw new ForegroundServiceStartNotAllowedException(msg);
                 }
@@ -2066,6 +2075,7 @@
 
             boolean alreadyStartedOp = false;
             boolean stopProcStatsOp = false;
+            final boolean origFgRequired = r.fgRequired;
             if (r.fgRequired) {
                 if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
                     Slog.i(TAG, "Service called startForeground() as required: " + r);
@@ -2117,6 +2127,9 @@
                 // Whether to extend the SHORT_SERVICE time out.
                 boolean extendShortServiceTimeout = false;
 
+                // Whether setFgsRestrictionLocked() is called in here. Only used for logging.
+                boolean fgsRestrictionRecalculated = false;
+
                 int fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_UNKNOWN;
                 if (!ignoreForeground) {
                     if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
@@ -2182,6 +2195,7 @@
                                 r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                 BackgroundStartPrivileges.NONE,
                                 false /* isBindService */);
+                        fgsRestrictionRecalculated = true;
                         if (!r.isFgsAllowedStart()) {
                             Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
                                     + " BFSL DENIED.");
@@ -2246,6 +2260,7 @@
                                         r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                         BackgroundStartPrivileges.NONE,
                                         false /* isBindService */);
+                                fgsRestrictionRecalculated = true;
                                 final String temp = "startForegroundDelayMs:" + delayMs;
                                 if (r.mInfoAllowStartForeground != null) {
                                     r.mInfoAllowStartForeground += "; " + temp;
@@ -2266,6 +2281,25 @@
                                 r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                 BackgroundStartPrivileges.NONE,
                                 false /* isBindService */);
+                        fgsRestrictionRecalculated = true;
+                    }
+
+                    // When startForeground() is called on a bound service, without having
+                    // it started (i.e. no Context.startService() or startForegroundService() was
+                    // called.)
+                    // called on it, then we probably didn't call setFgsRestrictionLocked()
+                    // in startService(). If fgsRestrictionRecalculated is false, then we
+                    // didn't call setFgsRestrictionLocked() here either.
+                    //
+                    // In this situation, we call setFgsRestrictionLocked() with
+                    // forBoundFgs = false, so we'd set the FGS allowed reason to the
+                    // by-bindings fields, so we can put it in the log, without affecting the
+                    // logic.
+                    if (!fgsRestrictionRecalculated && !r.startRequested) {
+                        setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
+                                r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+                                BackgroundStartPrivileges.NONE,
+                                false /* isBindService */, true /* forBoundFgs */);
                     }
 
                     // If the foreground service is not started from TOP process, do not allow it to
@@ -2291,7 +2325,10 @@
                             ignoreForeground = true;
                             logFGSStateChangeLocked(r,
                                     FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
-                                    0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+                                    0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                                    FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                                    false /* fgsRestrictionRecalculated */
+                            );
                             if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
                                     r.appInfo.uid)) {
                                 throw new ForegroundServiceStartNotAllowedException(msg);
@@ -2331,7 +2368,10 @@
                         if (fgsTypeResult.second != null) {
                             logFGSStateChangeLocked(r,
                                     FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
-                                    0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first);
+                                    0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first,
+                                    FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                                    false /* fgsRestrictionRecalculated */
+                            );
                             throw fgsTypeResult.second;
                         }
                     }
@@ -2403,9 +2443,24 @@
                                 AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
                         registerAppOpCallbackLocked(r);
                         mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
+
+                        int fgsStartApi = FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NONE;
+                        if (r.startRequested) {
+                            if (origFgRequired) {
+                                fgsStartApi =
+                                        FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_FOREGROUND_SERVICE;
+                            } else {
+                                fgsStartApi =
+                                        FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_START_SERVICE;
+                            }
+                        }
+
                         logFGSStateChangeLocked(r,
                                 FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
-                                0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode);
+                                0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode,
+                                fgsStartApi,
+                                fgsRestrictionRecalculated
+                        );
                         synchronized (mFGSLogger) {
                             mFGSLogger.logForegroundServiceStart(r.appInfo.uid, 0, r);
                         }
@@ -2499,7 +2554,10 @@
                         r.mFgsExitTime > r.mFgsEnterTime
                                 ? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
                         FGS_STOP_REASON_STOP_FOREGROUND,
-                        FGS_TYPE_POLICY_CHECK_UNKNOWN);
+                        FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                        FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                        false /* fgsRestrictionRecalculated */
+                );
 
                 synchronized (mFGSLogger) {
                     mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
@@ -3338,7 +3396,10 @@
                     FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
                     nowUptime > sr.mFgsEnterTime ? (int) (nowUptime - sr.mFgsEnterTime) : 0,
                     FGS_STOP_REASON_UNKNOWN,
-                    FGS_TYPE_POLICY_CHECK_UNKNOWN);
+                    FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                    FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                    false /* fgsRestrictionRecalculated */
+            );
             try {
                 sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId());
             } catch (RemoteException e) {
@@ -5705,7 +5766,10 @@
                     r.mFgsExitTime > r.mFgsEnterTime
                             ? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
                     FGS_STOP_REASON_STOP_SERVICE,
-                    FGS_TYPE_POLICY_CHECK_UNKNOWN);
+                    FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                    FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                    false /* fgsRestrictionRecalculated */
+            );
             synchronized (mFGSLogger) {
                 mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
             }
@@ -7452,6 +7516,13 @@
         }
     }
 
+    private void setFgsRestrictionLocked(String callingPackage,
+            int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
+            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
+                backgroundStartPrivileges, isBindService, /*forBoundFgs*/ false);
+    }
+
     /**
      * There are two FGS restrictions:
      * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground
@@ -7463,11 +7534,14 @@
      * @param intent intent to start/bind service.
      * @param r the service to start.
      * @param isBindService True if it's called from bindService().
+     * @param forBoundFgs set to true if it's called from Service.startForeground() for a
+     *                    service that's not started but bound.
      * @return true if allow, false otherwise.
      */
     private void setFgsRestrictionLocked(String callingPackage,
             int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
-            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+            boolean forBoundFgs) {
 
         @ReasonCode int allowWIU;
         @ReasonCode int allowStart;
@@ -7511,9 +7585,19 @@
             r.mAllowWIUInBindService = allowWIU;
             r.mAllowStartInBindService = allowStart;
         } else {
-            r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
-            r.mAllowStartForegroundNoBinding = allowStart;
-
+            if (!forBoundFgs) {
+                // This is for "normal" situation.
+                r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
+                r.mAllowStartForegroundNoBinding = allowStart;
+            } else {
+                // This logic is only for logging, so we only update the "by-binding" fields.
+                if (r.mAllowWIUByBindings == REASON_DENIED) {
+                    r.mAllowWIUByBindings = allowWIU;
+                }
+                if (r.mAllowStartByBindings == REASON_DENIED) {
+                    r.mAllowStartByBindings = allowStart;
+                }
+            }
             // Also do a binding client check, unless called from bindService().
             if (r.mAllowWIUByBindings == REASON_DENIED) {
                 r.mAllowWIUByBindings =
@@ -8137,7 +8221,10 @@
      */
     private void logFGSStateChangeLocked(ServiceRecord r, int state, int durationMs,
             @FgsStopReason int fgsStopReason,
-            @ForegroundServicePolicyCheckCode int fgsTypeCheckCode) {
+            @ForegroundServicePolicyCheckCode int fgsTypeCheckCode,
+            int fgsStartApi, // from ForegroundServiceStateChanged.FgsStartApi
+            boolean fgsRestrictionRecalculated
+    ) {
         if (!ActivityManagerUtils.shouldSamplePackageForAtom(
                 r.packageName, mAm.mConstants.mFgsAtomSampleRate)) {
             return;
@@ -8194,7 +8281,9 @@
                 r.mAllowWIUByBindings,
                 r.mAllowStartForegroundNoBinding,
                 r.mAllowStartInBindService,
-                r.mAllowStartByBindings);
+                r.mAllowStartByBindings,
+                fgsStartApi,
+                fgsRestrictionRecalculated);
 
         int event = 0;
         if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
@@ -8373,7 +8462,10 @@
         }
         logFGSStateChangeLocked(r,
                 FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
-                0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN);
+                0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_DELEGATE,
+                false /* fgsRestrictionRecalculated */
+        );
         // Notify the caller.
         if (connection != null) {
             mAm.mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b5911f6..cbc9ff1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -315,6 +315,7 @@
 import android.net.Proxy;
 import android.net.Uri;
 import android.os.AppZygote;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.BinderProxy;
@@ -4070,21 +4071,6 @@
                         profile.addPss(mi.getTotalPss(),
                                 mi.getTotalUss(), mi.getTotalRss(), false,
                                 ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration);
-                        proc.getPkgList().forEachPackageProcessStats(holder -> {
-                            final ProcessState state = holder.state;
-                            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
-                                    proc.info.uid,
-                                    state != null ? state.getName() : proc.processName,
-                                    state != null ? state.getPackage() : proc.info.packageName,
-                                    mi.getTotalPss(),
-                                    mi.getTotalUss(),
-                                    mi.getTotalRss(),
-                                    ProcessStats.ADD_PSS_EXTERNAL_SLOW,
-                                    duration,
-                                    holder.appVersion,
-                                    profile.getCurrentHostingComponentTypes(),
-                                    profile.getHistoricalHostingComponentTypes());
-                        });
                     }
                 }
             }
@@ -4131,20 +4117,6 @@
                         // Record this for posterity if the process has been stable.
                         profile.addPss(pi, tmpUss[0], tmpUss[2], false,
                                 ProcessStats.ADD_PSS_EXTERNAL, duration);
-                        proc.getPkgList().forEachPackageProcessStats(holder -> {
-                            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
-                                    proc.info.uid,
-                                    holder.state.getName(),
-                                    holder.state.getPackage(),
-                                    pi,
-                                    tmpUss[0],
-                                    tmpUss[2],
-                                    ProcessStats.ADD_PSS_EXTERNAL,
-                                    duration,
-                                    holder.appVersion,
-                                    profile.getCurrentHostingComponentTypes(),
-                                    profile.getHistoricalHostingComponentTypes());
-                        });
                     }
                 }
             }
@@ -9851,6 +9823,10 @@
         PriorityDump.dump(mPriorityDumper, fd, pw, args);
     }
 
+    private static final String TICK =
+            "---------------------------------------"
+            + "----------------------------------------";
+
     private void dumpEverything(FileDescriptor fd, PrintWriter pw, String[] args, int opti,
             boolean dumpAll, String dumpPackage, int displayIdFilter, boolean dumpClient,
             boolean dumpNormalPriority, int dumpAppId, boolean dumpProxies) {
@@ -9906,6 +9882,11 @@
                 sdumper.dumpLocked();
             }
         }
+
+        // No need to hold the lock.
+        pw.println(TICK);
+        AnrTimer.dump(pw, false);
+
         // We drop the lock here because we can't call dumpWithClient() with the lock held;
         // if the caller wants a consistent state for the !dumpClient case, it can call this
         // method with the lock held.
@@ -10351,6 +10332,8 @@
                     mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
                     mOomAdjuster.dumpCacheOomRankerSettings(pw);
                 }
+            } else if ("timers".equals(cmd)) {
+                AnrTimer.dump(pw, true);
             } else if ("services".equals(cmd) || "s".equals(cmd)) {
                 if (dumpClient) {
                     ActiveServices.ServiceDumper dumper;
@@ -12077,17 +12060,6 @@
                         // Record this for posterity if the process has been stable.
                         r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
                                 reportType, endTime - startTime);
-                        r.getPkgList().forEachPackageProcessStats(holder -> {
-                            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
-                                    r.info.uid,
-                                    holder.state.getName(),
-                                    holder.state.getPackage(),
-                                    myTotalPss, myTotalUss, myTotalRss, reportType,
-                                    endTime-startTime,
-                                    holder.appVersion,
-                                    r.mProfile.getCurrentHostingComponentTypes(),
-                                    r.mProfile.getHistoricalHostingComponentTypes());
-                        });
                     }
                 }
 
@@ -12723,16 +12695,6 @@
                     // Record this for posterity if the process has been stable.
                     r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
                                 reportType, endTime - startTime);
-                    r.getPkgList().forEachPackageProcessStats(holder -> {
-                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
-                                r.info.uid,
-                                holder.state.getName(),
-                                holder.state.getPackage(),
-                                myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
-                                holder.appVersion,
-                                r.mProfile.getCurrentHostingComponentTypes(),
-                                r.mProfile.getHistoricalHostingComponentTypes());
-                    });
                 }
             }
 
@@ -15134,6 +15096,16 @@
             }
         }
 
+        // STOPSHIP(b/298884211):  Remove this logging
+        if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+            final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+            if (level < 0) {
+                Slog.wtf(BroadcastQueue.TAG, "Unexpected broadcast: " + intent
+                        + "; callingUid: " + callingUid + ", callingPid: " + callingPid,
+                        new Throwable());
+            }
+        }
+
         int[] users;
         if (userId == UserHandle.USER_ALL) {
             // Caller wants broadcast to go to all started users.
@@ -15866,7 +15838,15 @@
         activeInstr.mWatcher = watcher;
         activeInstr.mUiAutomationConnection = uiAutomationConnection;
         activeInstr.mResultClass = className;
-        activeInstr.mHasBackgroundActivityStartsPermission = false;
+        activeInstr.mHasBackgroundActivityStartsPermission =
+                isSdkInSandbox
+                        // TODO(b/261864298): consider using START_ACTIVITIES_FROM_BACKGROUND.
+                        && checkPermission(
+                                        android.Manifest.permission
+                                                .START_ACTIVITIES_FROM_SDK_SANDBOX,
+                                        Binder.getCallingPid(),
+                                        Binder.getCallingUid())
+                                == PackageManager.PERMISSION_GRANTED;
         activeInstr.mHasBackgroundForegroundServiceStartsPermission = false;
         // Instrumenting sdk sandbox without a restart is not supported
         activeInstr.mNoRestart = false;
@@ -16863,7 +16843,7 @@
             for (int i = 0; i < N; i++) {
                 PendingTempAllowlist ptw = list[i];
                 mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
-                        ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag,
+                        ptw.duration, ptw.type, false, ptw.reasonCode, ptw.tag,
                         ptw.callingUid);
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 942d35a..a057f32 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -4095,6 +4095,7 @@
             pw.println("    lru: raw LRU process list");
             pw.println("    binder-proxies: stats on binder objects and IPCs");
             pw.println("    settings: currently applied config settings");
+            pw.println("    timers: the current ANR timer state");
             pw.println("    service [COMP_SPEC]: service client-side state");
             pw.println("    package [PACKAGE_NAME]: all state related to given package");
             pw.println("    all: dump all activities");
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 7d98443..e0a2246 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -20,6 +20,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.content.pm.ApplicationInfo;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.ArraySet;
@@ -267,6 +268,7 @@
     private class AnrRecord {
         final ProcessRecord mApp;
         final int mPid;
+        final int mUid;
         final String mActivityShortComponentName;
         final String mParentShortComponentName;
         final TimeoutRecord mTimeoutRecord;
@@ -283,6 +285,7 @@
                 Future<File> firstPidFilePromise) {
             mApp = anrProcess;
             mPid = anrProcess.mPid;
+            mUid = anrProcess.uid;
             mActivityShortComponentName = activityShortComponentName;
             mParentShortComponentName = parentShortComponentName;
             mTimeoutRecord = timeoutRecord;
diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
new file mode 100644
index 0000000..378a386
--- /dev/null
+++ b/services/core/java/com/android/server/am/AnrTimer.java
@@ -0,0 +1,830 @@
+/*
+ * 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.am;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Keep;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.RingBuffer;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class managers AnrTimers.  An AnrTimer is a substitute for a delayed Message.  In legacy
+ * mode, the timer just sends a delayed message.  In modern mode, the timer is implemented in
+ * native code; on expiration, the message is sent without delay.
+ *
+ * <p>There are four external operations on a timer:
+ * <ul>
+ *
+ * <li>{@link #start} starts a timer.  The timer is started with an object that the message
+ * argument.  The timer is also given the pid and uid of the target. A timer that is started must
+ * be canceled, accepted, or discarded.
+ *
+ * <li>{@link #cancel} stops a timer and removes any in-flight expiration messages.
+ *
+ * <li>{@link #accept} acknowledges that the timer has expired, and that an ANR should be
+ * generated.  This clears bookkeeping information for the timer.
+ *
+ * <li>{@link #discard} acknowledges that the timer has expired but, for other reasons, no ANR
+ * will be generated.  This clears bookkeeping information for the timer.
+ *
+ *</li></p>
+ *
+ * <p>There is one internal operation on a timer: {@link #expire}.  A timer may have automatic
+ * extensions enabled.  If so, the extension is computed and if the extension is non-zero, the timer
+ * is restarted with the extension timeout.  If extensions are disabled or if the extension is zero,
+ * the client process is notified of the expiration.
+ *
+ * @hide
+ */
+class AnrTimer<V> {
+
+    /**
+     * The log tag.
+     */
+    final static String TAG = "AnrTimer";
+
+    /**
+     * The trace track for these events.  There is a single track for all AnrTimer instances.  The
+     * tracks give a sense of handler latency: the time between timer expiration and ANR
+     * collection.
+     */
+    private final static String TRACK = "AnrTimer";
+
+    /**
+     * Enable debug messages.
+     */
+    private static boolean DEBUG = false;
+
+    /**
+     * The trace tag.
+     */
+    private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+    /**
+     * Enable tracing from the time a timer expires until it is accepted or discarded.  This is
+     * used to diagnose long latencies in the client.
+     */
+    private static final boolean ENABLE_TRACING = false;
+
+    /**
+     * The status of an ANR timer.  TIMER_INVALID status is returned when an error is detected.
+     */
+    private static final int TIMER_INVALID = 0;
+    private static final int TIMER_RUNNING = 1;
+    private static final int TIMER_EXPIRED = 2;
+
+    @IntDef(prefix = { "TIMER_" }, value = {
+                TIMER_INVALID, TIMER_RUNNING, TIMER_EXPIRED
+            })
+    private @interface TimerStatus {}
+
+    /**
+     * A static list of all known AnrTimer instances, used for dumping and testing.
+     */
+    @GuardedBy("sAnrTimerList")
+    private static final ArrayList<WeakReference<AnrTimer>> sAnrTimerList = new ArrayList<>();
+
+    /**
+     * An error is defined by its issue, the operation that detected the error, the tag of the
+     * affected service, a short stack of the bad call, and the stringified arg associated with
+     * the error.
+     */
+    private static final class Error {
+        /** The issue is the kind of error that was detected.  This is a free-form string. */
+        final String issue;
+        /** The operation that detected the error: start, cancel, accept, or discard. */
+        final String operation;
+        /** The argument (stringified) passed in to the operation. */
+        final String arg;
+        /** The tag of the associated AnrTimer. */
+        final String tag;
+        /** A partial stack that localizes the caller of the operation. */
+        final StackTraceElement[] stack;
+        /** The date, in local time, the error was created. */
+        final String date;
+
+        Error(@NonNull String issue, @NonNull String operation, @NonNull String tag,
+                @NonNull StackTraceElement[] stack, @NonNull String arg) {
+            this.issue = issue;
+            this.operation = operation;
+            this.tag = tag;
+            this.stack = stack;
+            this.arg = arg;
+            this.date = new Date().toString();
+        }
+    }
+
+    /**
+     * A list of errors detected during processing.  Errors correspond to "timer not found"
+     * conditions.  The stack trace identifies the source of the call.  The list is
+     * first-in/first-out, and the size is limited to 20.
+     */
+    @GuardedBy("sErrors")
+    private static final RingBuffer<Error> sErrors = new RingBuffer<>(Error.class, 20);
+
+    /**
+     * A record of a single anr timer.  The pid and uid are retained for reference but they do not
+     * participate in the equality tests. A {@link Timer} is bound to its parent {@link AnrTimer}
+     * through the owner field.  Access to timer fields is guarded by the mLock of the owner.
+     */
+    private static class Timer {
+        /** The AnrTimer that is managing this Timer. */
+        final AnrTimer owner;
+
+        /** The argument that uniquely identifies the Timer in the context of its current owner. */
+        final Object arg;
+        /** The pid of the process being tracked by this Timer. */
+        final int pid;
+        /** The uid of the process being tracked by this Timer as reported by the kernel. */
+        final int uid;
+        /** The original timeout. */
+        final long timeoutMs;
+
+        /** The status of the Timer.  */
+        @GuardedBy("owner.mLock")
+        @TimerStatus
+        int status;
+
+        /** The absolute time the timer was startd */
+        final long startedMs;
+
+        /** Fields used by the native timer service. */
+
+        /** The timer ID: used to exchange information with the native service. */
+        int timerId;
+
+        /** Fields used by the legacy timer service. */
+
+        /**
+         * The process's cpu delay time when the timer starts . It is meaningful only if
+         * extendable is true.  The cpu delay is cumulative, so the incremental delay that occurs
+         * during a timer is the delay at the end of the timer minus this value.  Units are in
+         * milliseconds.
+         */
+        @GuardedBy("owner.mLock")
+        long initialCpuDelayMs;
+
+        /** True if the timer has been extended. */
+        @GuardedBy("owner.mLock")
+        boolean extended;
+
+        /**
+         * Fetch a new Timer.  This is private.  Clients should get a new timer using the obtain()
+         * method.
+         */
+        private Timer(int pid, int uid, @Nullable Object arg, long timeoutMs,
+                @NonNull AnrTimer service) {
+            this.arg = arg;
+            this.pid = pid;
+            this.uid = uid;
+            this.timerId = 0;
+            this.timeoutMs = timeoutMs;
+            this.startedMs = now();
+            this.owner = service;
+            this.initialCpuDelayMs = 0;
+            this.extended = false;
+            this.status = TIMER_INVALID;
+        }
+
+        /** Get a timer.  This implementation constructs a new timer. */
+        static Timer obtain(int pid, int uid, @Nullable Object arg, long timeout,
+                @NonNull AnrTimer service) {
+            return new Timer(pid, uid, arg, timeout, service);
+        }
+
+        /** Release a timer. This implementation simply drops the timer. */
+        void release() {
+        }
+
+        /** Return the age of the timer. This is used for debugging. */
+        long age() {
+            return now() - startedMs;
+        }
+
+        /**
+         * The hash code is generated from the owner and the argument.  By definition, the
+         * combination must be unique for the lifetime of an in-use Timer.
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hash(owner, arg);
+        }
+
+        /**
+         * The equality check compares the owner and the argument.  By definition, the combination
+         * must be unique for the lifetime of an in-use Timer.
+         */
+        @Override
+        public boolean equals(Object r) {
+            if (r instanceof Timer) {
+                Timer t = (Timer) r;
+                return Objects.equals(owner, t.owner) && Objects.equals(arg, t.arg);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            final int myStatus;
+            synchronized (owner.mLock) {
+                myStatus = status;
+            }
+            return "timerId=" + timerId + " pid=" + pid + " uid=" + uid
+                    + " " + statusString(myStatus) + " " + owner.mLabel;
+        }
+    }
+
+    /** A lock for the AnrTimer instance. */
+    private final Object mLock = new Object();
+
+    /**
+     * The map from client argument to the associated timer.
+     */
+    @GuardedBy("mLock")
+    private final ArrayMap<V, Timer> mTimerMap = new ArrayMap<>();
+
+    /** The highwater mark of started, but not closed, timers. */
+    @GuardedBy("mLock")
+    private int mMaxStarted = 0;
+
+    /**
+     * The total number of timers started.
+     */
+    @GuardedBy("mLock")
+    private int mTotalStarted = 0;
+
+    /**
+     * The total number of errors detected.
+     */
+    @GuardedBy("mLock")
+    private int mTotalErrors = 0;
+
+    /**
+     * The total number of timers that have expired.
+     */
+    @GuardedBy("mLock")
+    private int mTotalExpired = 0;
+
+    /**
+     * A TimerService that generates a timeout event <n> milliseconds in the future.  See the
+     * class documentation for an explanation of the operations.
+     */
+    private abstract class TimerService {
+        /** Start a timer.  The timeout must be initialized. */
+        abstract boolean start(@NonNull Timer timer);
+
+        abstract void cancel(@NonNull Timer timer);
+
+        abstract void accept(@NonNull Timer timer);
+
+        abstract void discard(@NonNull Timer timer);
+    }
+
+    /**
+     * A class to assist testing.  All methods are null by default but can be overridden as
+     * necessary for a test.
+     */
+    @VisibleForTesting
+    static class Injector {
+        /**
+         * Return a handler for the given Callback.
+         */
+        Handler getHandler(@NonNull Handler.Callback callback) {
+            return null;
+        };
+
+        /**
+         * Return a CpuTracker.
+         */
+        CpuTracker getTracker() {
+            return null;
+        }
+    }
+
+    /**
+     * A helper class to measure CPU delays.  Given a process ID, this class will return the
+     * cumulative CPU delay for the PID, since process inception.  This class is defined to assist
+     * testing.
+     */
+    @VisibleForTesting
+    static class CpuTracker {
+        /**
+         * The parameter to ProcessCpuTracker indicates that statistics should be collected on a
+         * single process and not on the collection of threads associated with that process.
+         */
+        private final ProcessCpuTracker mCpu = new ProcessCpuTracker(false);
+
+        /** A simple wrapper to fetch the delay.  This method can be overridden for testing. */
+        long delay(int pid) {
+            return mCpu.getCpuDelayTimeForPid(pid);
+        }
+    }
+
+    /**
+     * The "user-space" implementation of the timer service.  This service uses its own message
+     * handler to create timeouts.
+     */
+    private class HandlerTimerService extends TimerService {
+        /** The lock for this handler */
+        private final Object mLock = new Object();
+
+        /** The message handler for scheduling future events. */
+        private final Handler mHandler;
+
+        /** The interface to fetch process statistics that might extend an ANR timeout. */
+        private final CpuTracker mCpu;
+
+        /** Create a HandlerTimerService based on the input handler. */
+        HandlerTimerService(@NonNull Handler handler) {
+            mHandler = new Handler(handler.getLooper(), this::expires);
+            mCpu = new CpuTracker();
+        }
+
+        /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */
+        @VisibleForTesting
+        HandlerTimerService(@NonNull Injector injector) {
+            mHandler = injector.getHandler(this::expires);
+            mCpu = injector.getTracker();
+        }
+
+        /** Post a message with the specified timeout.  The timer is not modified. */
+        private void post(@NonNull Timer t, long timeoutMillis) {
+            final Message msg = mHandler.obtainMessage();
+            msg.obj = t;
+            mHandler.sendMessageDelayed(msg, timeoutMillis);
+        }
+
+        /**
+         * The local expiration handler first attempts to compute a timer extension.  If the timer
+         * should be extended, it is rescheduled in the future (granting more time to the
+         * associated process).  If the timer should not be extended then the timeout is delivered
+         * to the client.
+         *
+         * A process is extended to account for the time the process was swapped out and was not
+         * runnable through no fault of its own.  A timer can only be extended once and only if
+         * the AnrTimer permits extensions.  Finally, a timer will never be extended by more than
+         * the original timeout, so the total timeout will never be more than twice the originally
+         * configured timeout.
+         */
+        private boolean expires(Message msg) {
+            Timer t = (Timer) msg.obj;
+            synchronized (mLock) {
+                long extension = 0;
+                if (mExtend && !t.extended) {
+                    extension = mCpu.delay(t.pid) - t.initialCpuDelayMs;
+                    if (extension < 0) extension = 0;
+                    if (extension > t.timeoutMs) extension = t.timeoutMs;
+                    t.extended = true;
+                }
+                if (extension > 0) {
+                    post(t, extension);
+                } else {
+                    onExpiredLocked(t);
+                }
+            }
+            return true;
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        boolean start(@NonNull Timer t) {
+            if (mExtend) {
+                t.initialCpuDelayMs = mCpu.delay(t.pid);
+            }
+            post(t, t.timeoutMs);
+            return true;
+        }
+
+        @Override
+        void cancel(@NonNull Timer t) {
+            mHandler.removeMessages(0, t);
+        }
+
+        @Override
+        void accept(@NonNull Timer t) {
+            // Nothing to do.
+        }
+
+        @Override
+        void discard(@NonNull Timer t) {
+            // Nothing to do.
+        }
+
+        /** The string identifies this subclass of AnrTimerService as being based on handlers. */
+        @Override
+        public String toString() {
+            return "handler";
+        }
+    }
+
+    /**
+     * The handler for messages sent from this instance.
+     */
+    private final Handler mHandler;
+
+    /**
+     * The message type for messages sent from this interface.
+     */
+    private final int mWhat;
+
+    /**
+     * A label that identifies the AnrTimer associated with a Timer in log messages.
+     */
+    private final String mLabel;
+
+    /**
+     * Whether this timer instance supports extending timeouts.
+     */
+    private final boolean mExtend;
+
+    /**
+     * The timer service to use for this AnrTimer.
+     */
+    private final TimerService mTimerService;
+
+    /**
+     * Whether or not canceling a non-existent timer is an error.  Clients often cancel freely
+     * preemptively, without knowing if the timer was ever started.  Keeping this variable true
+     * means that such behavior is not an error.
+     */
+    private final boolean mLenientCancel = true;
+
+    /**
+     * The common constructor.  A null injector results in a normal, production timer.
+     */
+    @VisibleForTesting
+    AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend,
+            @Nullable Injector injector) {
+        mHandler = handler;
+        mWhat = what;
+        mLabel = label;
+        mExtend = extend;
+        if (injector == null) {
+            mTimerService = new HandlerTimerService(handler);
+        } else {
+            mTimerService = new HandlerTimerService(injector);
+        }
+        synchronized (sAnrTimerList) {
+            sAnrTimerList.add(new WeakReference(this));
+        }
+        Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService.toString(), label));
+    }
+
+    /**
+     * Create one timer instance for production.  The client can ask for extensible timeouts.
+     */
+    AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) {
+        this(handler, what, label, extend, null);
+    }
+
+    /**
+     * Create one timer instance for production.  There are no extensible timeouts.
+     */
+    AnrTimer(@NonNull Handler handler, int what, @NonNull String label) {
+        this(handler, what, label, false, null);
+    }
+
+    /**
+     * Start a trace on the timer.  The trace is laid down in the AnrTimerTrack.
+     */
+    private void traceBegin(Timer t, String what) {
+        if (ENABLE_TRACING) {
+            final String label = formatSimple("%s(%d,%d,%s)", what, t.pid, t.uid, mLabel);
+            final int cookie = t.hashCode();
+            Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK, label, cookie);
+        }
+    }
+
+    /**
+     * End a trace on the timer.
+     */
+    private void traceEnd(Timer t) {
+        if (ENABLE_TRACING) {
+            final int cookie = t.hashCode();
+            Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK, cookie);
+        }
+    }
+
+    /**
+     * Return the string representation for a timer status.
+     */
+    private static String statusString(int s) {
+        switch (s) {
+            case TIMER_INVALID: return "invalid";
+            case TIMER_RUNNING: return "running";
+            case TIMER_EXPIRED: return "expired";
+        }
+        return formatSimple("unknown: %d", s);
+    }
+
+    /**
+     * Delete the timer associated with arg from the maps and return it.  Return null if the timer
+     * was not found.
+     */
+    @GuardedBy("mLock")
+    private Timer removeLocked(V arg) {
+        Timer timer = mTimerMap.remove(arg);
+        return timer;
+    }
+
+    /**
+     * Return the number of timers currently running.
+     */
+    @VisibleForTesting
+    static int sizeOfTimerList() {
+        synchronized (sAnrTimerList) {
+            int totalTimers = 0;
+            for (int i = 0; i < sAnrTimerList.size(); i++) {
+                AnrTimer client = sAnrTimerList.get(i).get();
+                if (client != null) totalTimers += client.mTimerMap.size();
+            }
+            return totalTimers;
+        }
+    }
+
+    /**
+     * Clear out all existing timers.  This will lead to unexpected behavior if used carelessly.
+     * It is available only for testing.  It returns the number of times that were actually
+     * erased.
+     */
+    @VisibleForTesting
+    static int resetTimerListForHermeticTest() {
+        synchronized (sAnrTimerList) {
+            int mapLen = 0;
+            for (int i = 0; i < sAnrTimerList.size(); i++) {
+                AnrTimer client = sAnrTimerList.get(i).get();
+                if (client != null) {
+                    mapLen += client.mTimerMap.size();
+                    client.mTimerMap.clear();
+                }
+            }
+            if (mapLen > 0) {
+                Log.w(TAG, formatSimple("erasing timer list: clearing %d timers", mapLen));
+            }
+            return mapLen;
+        }
+    }
+
+    /**
+     * Report something about a timer.
+     */
+    private void report(@NonNull Timer timer, @NonNull String msg) {
+        Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg));
+    }
+
+   /**
+     * Start a timer.
+     */
+    boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+        final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, this);
+        synchronized (mLock) {
+            Timer old = mTimerMap.get(arg);
+            if (old != null) {
+                // There is an existing timer.  This is a protocol error in the client.  Record
+                // the error and then clean up by canceling running timers and discarding expired
+                // timers.
+                restartedLocked(old.status, arg);
+                if (old.status == TIMER_EXPIRED) {
+                    discard(arg);
+                } else {
+                    cancel(arg);
+                }
+            }
+            if (mTimerService.start(timer)) {
+                timer.status = TIMER_RUNNING;
+                mTimerMap.put(arg, timer);
+                mTotalStarted++;
+                mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
+                if (DEBUG) report(timer, "start");
+                return true;
+            } else {
+                Log.e(TAG, "AnrTimer.start failed");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Cancel a timer.  Return false if the timer was not found.
+     */
+    boolean cancel(@NonNull V arg) {
+        synchronized (mLock) {
+            Timer timer = removeLocked(arg);
+            if (timer == null) {
+                if (!mLenientCancel) notFoundLocked("cancel", arg);
+                return false;
+            }
+            mTimerService.cancel(timer);
+            // There may be an expiration message in flight.  Cancel it.
+            mHandler.removeMessages(mWhat, arg);
+            if (DEBUG) report(timer, "cancel");
+            timer.release();
+            return true;
+        }
+    }
+
+    /**
+     * Accept a timer in the framework-level handler.  The timeout has been accepted and the
+     * timeout handler is executing.  Return false if the timer was not found.
+     */
+    boolean accept(@NonNull V arg) {
+        synchronized (mLock) {
+            Timer timer = removeLocked(arg);
+            if (timer == null) {
+                notFoundLocked("accept", arg);
+                return false;
+            }
+            mTimerService.accept(timer);
+            traceEnd(timer);
+            if (DEBUG) report(timer, "accept");
+            timer.release();
+            return true;
+        }
+    }
+
+    /**
+     * Discard a timer in the framework-level handler.  For whatever reason, the timer is no
+     * longer interesting.  No statistics are collected.  Return false if the time was not found.
+     */
+    boolean discard(@NonNull V arg) {
+        synchronized (mLock) {
+            Timer timer = removeLocked(arg);
+            if (timer == null) {
+                notFoundLocked("discard", arg);
+                return false;
+            }
+            mTimerService.discard(timer);
+            traceEnd(timer);
+            if (DEBUG) report(timer, "discard");
+            timer.release();
+            return true;
+        }
+    }
+
+    /**
+     * The notifier that a timer has fired.  The timer is not modified.
+     */
+    @GuardedBy("mLock")
+    private void onExpiredLocked(@NonNull Timer timer) {
+        if (DEBUG) report(timer, "expire");
+        traceBegin(timer, "expired");
+        mHandler.sendMessage(Message.obtain(mHandler, mWhat, timer.arg));
+        synchronized (mLock) {
+            mTotalExpired++;
+        }
+    }
+
+    /**
+     * Dump a single AnrTimer.
+     */
+    private void dump(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.format("timer: %s\n", mLabel);
+            pw.increaseIndent();
+            pw.format("started=%d maxStarted=%d running=%d expired=%d error=%d\n",
+                    mTotalStarted, mMaxStarted, mTimerMap.size(),
+                    mTotalExpired, mTotalErrors);
+            pw.decreaseIndent();
+        }
+    }
+
+    /**
+     * Enable or disable debugging.
+     */
+    static void debug(boolean f) {
+        DEBUG = f;
+    }
+
+    /**
+     * The current time in milliseconds.
+     */
+    private static long now() {
+        return SystemClock.uptimeMillis();
+    }
+
+    /**
+     * Log an error.  A limited stack trace leading to the client call that triggered the error is
+     * recorded.  The stack trace assumes that this method is not called directly.
+     *
+     * If DEBUG is true, a log message is generated as well.
+     */
+    @GuardedBy("mLock")
+    private void recordErrorLocked(String operation, String errorMsg, Object arg) {
+        StackTraceElement[] s = Thread.currentThread().getStackTrace();
+        final String what = Objects.toString(arg);
+        // The copy range starts at the caller of the timer operation, and includes three levels.
+        // This should be enough to isolate the location of the call.
+        StackTraceElement[] location = Arrays.copyOfRange(s, 6, 9);
+        synchronized (sErrors) {
+            sErrors.append(new Error(errorMsg, operation, mLabel, location, what));
+        }
+        if (DEBUG) Log.w(TAG, operation + " " + errorMsg + " " + mLabel + " timer " + what);
+        mTotalErrors++;
+    }
+
+    /**
+     * Log an error about  a timer not found.
+     */
+    @GuardedBy("mLock")
+    private void notFoundLocked(String operation, Object arg) {
+        recordErrorLocked(operation, "notFound", arg);
+    }
+
+    /**
+     * Log an error about a timer that is started when there is an existing timer.
+     */
+    @GuardedBy("mLock")
+    private void restartedLocked(@TimerStatus int status, Object arg) {
+        recordErrorLocked("start", status == TIMER_EXPIRED ? "autoDiscard" : "autoCancel", arg);
+    }
+
+    /**
+     * Dump a single error to the output stream.
+     */
+    private static void dump(IndentingPrintWriter ipw, int seq, Error err) {
+        ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, err.operation, err.tag,
+                err.issue, err.arg);
+        ipw.format("    date:%s\n", err.date);
+        ipw.increaseIndent();
+        for (int i = 0; i < err.stack.length; i++) {
+            ipw.println("    " + err.stack[i].toString());
+        }
+        ipw.decreaseIndent();
+    }
+
+    /**
+     * Dump all errors to the output stream.
+     */
+    private static void dumpErrors(IndentingPrintWriter ipw) {
+        Error errors[];
+        synchronized (sErrors) {
+            if (sErrors.size() == 0) return;
+            errors = sErrors.toArray();
+        }
+        ipw.println("Errors");
+        ipw.increaseIndent();
+        for (int i = 0; i < errors.length; i++) {
+            if (errors[i] != null) dump(ipw, i, errors[i]);
+        }
+        ipw.decreaseIndent();
+    }
+
+    /**
+     * Dumpsys output.
+     */
+    static void dump(@NonNull PrintWriter pw, boolean verbose) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+        ipw.println("AnrTimer statistics");
+        ipw.increaseIndent();
+        synchronized (sAnrTimerList) {
+            for (int i = 0; i < sAnrTimerList.size(); i++) {
+                AnrTimer client = sAnrTimerList.get(i).get();
+                if (client != null) client.dump(ipw);
+            }
+        }
+        if (verbose) dumpErrors(ipw);
+        ipw.format("AnrTimerEnd\n");
+        ipw.decreaseIndent();
+    }
+}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 46e5523..928b5d8 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -770,17 +770,6 @@
                 swapPss * 1024, rss * 1024, statType, procState, pssDuration);
         profile.setLastPssTime(now);
         profile.addPss(pss, uss, rss, true, statType, pssDuration);
-        proc.getPkgList().forEachPackageProcessStats(holder -> {
-            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
-                    proc.info.uid,
-                    holder.state.getName(),
-                    holder.state.getPackage(),
-                    pss, uss, rss,
-                    statType, pssDuration,
-                    holder.appVersion,
-                    profile.getCurrentHostingComponentTypes(),
-                    profile.getHistoricalHostingComponentTypes());
-        });
         if (DEBUG_PSS) {
             Slog.d(TAG_PSS,
                     "pss of " + proc.toShortString() + ": " + pss
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index fc8175b..5d31d15 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -101,10 +101,9 @@
     boolean runningOomAdjusted;
 
     /**
-     * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically
-     * used when deciding if we should extend the soft ANR timeout.
+     * True if a timer has been started against this queue.
      */
-    long lastCpuDelayTime;
+    private boolean mTimeoutScheduled;
 
     /**
      * Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before
@@ -1344,6 +1343,21 @@
         return head;
     }
 
+    /**
+     * Set the timeout flag to indicate that an ANR timer has been started.  A value of true means a
+     * timer is running; a value of false means there is no timer running.
+     */
+    void setTimeoutScheduled(boolean timeoutScheduled) {
+        mTimeoutScheduled = timeoutScheduled;
+    }
+
+    /**
+     * Get the timeout flag
+     */
+    boolean timeoutScheduled() {
+        return mTimeoutScheduled;
+    }
+
     @Override
     public String toString() {
         if (mCachedToString == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8d2edaa..a3dac6d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -59,6 +59,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.BatteryManager;
 import android.os.Bundle;
 import android.os.BundleMerger;
 import android.os.Handler;
@@ -149,6 +150,8 @@
         // We configure runnable size only once at boot; it'd be too complex to
         // try resizing dynamically at runtime
         mRunning = new BroadcastProcessQueue[mConstants.getMaxRunningQueues()];
+
+        mAnrTimer = new BroadcastAnrTimer(mLocalHandler);
     }
 
     /**
@@ -242,14 +245,19 @@
      */
     private @UptimeMillisLong long mLastTestFailureTime;
 
+    /**
+     * The ANR timer service for broadcasts.
+     */
+    @GuardedBy("mService")
+    private final BroadcastAnrTimer mAnrTimer;
+
     private static final int MSG_UPDATE_RUNNING_LIST = 1;
-    private static final int MSG_DELIVERY_TIMEOUT_SOFT = 2;
-    private static final int MSG_DELIVERY_TIMEOUT_HARD = 3;
-    private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 4;
-    private static final int MSG_CHECK_HEALTH = 5;
-    private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 6;
-    private static final int MSG_PROCESS_FREEZABLE_CHANGED = 7;
-    private static final int MSG_UID_STATE_CHANGED = 8;
+    private static final int MSG_DELIVERY_TIMEOUT = 2;
+    private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 3;
+    private static final int MSG_CHECK_HEALTH = 4;
+    private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 5;
+    private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6;
+    private static final int MSG_UID_STATE_CHANGED = 7;
 
     private void enqueueUpdateRunningList() {
         mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
@@ -264,12 +272,8 @@
                 updateRunningList();
                 return true;
             }
-            case MSG_DELIVERY_TIMEOUT_SOFT: {
-                deliveryTimeoutSoft((BroadcastProcessQueue) msg.obj, msg.arg1);
-                return true;
-            }
-            case MSG_DELIVERY_TIMEOUT_HARD: {
-                deliveryTimeoutHard((BroadcastProcessQueue) msg.obj);
+            case MSG_DELIVERY_TIMEOUT: {
+                deliveryTimeout((BroadcastProcessQueue) msg.obj);
                 return true;
             }
             case MSG_BG_ACTIVITY_START_TIMEOUT: {
@@ -1024,12 +1028,12 @@
         // immediately assume delivery success
         final boolean assumeDelivered = r.isAssumedDelivered(index);
         if (mService.mProcessesReady && !r.timeoutExempt && !assumeDelivered) {
-            queue.lastCpuDelayTime = queue.app.getCpuDelayTime();
-
+            queue.setTimeoutScheduled(true);
             final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT
                     : mBgConstants.TIMEOUT);
-            mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler,
-                    MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);
+            mAnrTimer.start(queue, softTimeoutMillis);
+        } else {
+            queue.setTimeoutScheduled(false);
         }
 
         if (r.mBackgroundStartPrivileges.allowsAny()) {
@@ -1074,6 +1078,16 @@
                 queue.lastProcessState = app.mState.getCurProcState();
                 if (receiver instanceof BroadcastFilter) {
                     notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
+                    // STOPSHIP(b/298884211):  Remove this logging
+                    if (Intent.ACTION_BATTERY_CHANGED.equals(receiverIntent.getAction())) {
+                        int level = receiverIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+                        if (level < 0) {
+                            Slog.wtf(TAG, "Dispatching unexpected broadcast: " + receiverIntent
+                                    + " to " + receiver
+                                    + "; callingUid: " + r.callingUid
+                                    + ", callingPid: " + r.callingPid);
+                        }
+                    }
                     thread.scheduleRegisteredReceiver(
                         ((BroadcastFilter) receiver).receiverList.receiver,
                         receiverIntent, r.resultCode, r.resultData, r.resultExtras,
@@ -1107,7 +1121,7 @@
                 // If we were trying to deliver a manifest broadcast, throw the error as we need
                 // to try redelivering the broadcast to this receiver.
                 if (receiver instanceof ResolveInfo) {
-                    mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
+                    mAnrTimer.cancel(queue);
                     throw new BroadcastDeliveryFailedException(e);
                 }
                 finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
@@ -1156,41 +1170,29 @@
         r.resultTo = null;
     }
 
-    private void deliveryTimeoutSoft(@NonNull BroadcastProcessQueue queue,
-            int softTimeoutMillis) {
+    private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) {
         synchronized (mService) {
-            deliveryTimeoutSoftLocked(queue, softTimeoutMillis);
+            deliveryTimeoutLocked(queue);
         }
     }
 
-    private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue,
-            int softTimeoutMillis) {
-        if (queue.app != null) {
-            // Instead of immediately triggering an ANR, extend the timeout by
-            // the amount of time the process was runnable-but-waiting; we're
-            // only willing to do this once before triggering an hard ANR
-            final long cpuDelayTime = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime;
-            final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis);
-            mLocalHandler.sendMessageDelayed(
-                    Message.obtain(mLocalHandler, MSG_DELIVERY_TIMEOUT_HARD, queue),
-                    hardTimeoutMillis);
-        } else {
-            deliveryTimeoutHardLocked(queue);
-        }
-    }
-
-    private void deliveryTimeoutHard(@NonNull BroadcastProcessQueue queue) {
-        synchronized (mService) {
-            deliveryTimeoutHardLocked(queue);
-        }
-    }
-
-    private void deliveryTimeoutHardLocked(@NonNull BroadcastProcessQueue queue) {
+    private void deliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) {
         finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_TIMEOUT,
-                "deliveryTimeoutHardLocked");
+                "deliveryTimeoutLocked");
         demoteFromRunningLocked(queue);
     }
 
+    private class BroadcastAnrTimer extends AnrTimer<BroadcastProcessQueue> {
+        BroadcastAnrTimer(@NonNull Handler handler) {
+            super(Objects.requireNonNull(handler),
+                    MSG_DELIVERY_TIMEOUT, "BROADCAST_TIMEOUT", true);
+        }
+
+        void start(@NonNull BroadcastProcessQueue queue, long timeoutMillis) {
+            start(queue, queue.app.getPid(), queue.app.uid, timeoutMillis);
+        }
+    }
+
     @Override
     public boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode,
             @Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
@@ -1292,14 +1294,16 @@
         if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
             r.anrCount++;
             if (app != null && !app.isDebugging()) {
+                mAnrTimer.accept(queue);
                 final String packageName = getReceiverPackageName(receiver);
                 final String className = getReceiverClassName(receiver);
                 mService.appNotResponding(queue.app,
                         TimeoutRecord.forBroadcastReceiver(r.intent, packageName, className));
+            } else {
+                mAnrTimer.discard(queue);
             }
-        } else {
-            mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
-            mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_HARD, queue);
+        } else if (queue.timeoutScheduled()) {
+            mAnrTimer.cancel(queue);
         }
 
         // Given that a receiver just finished, check if the "waitingFor" conditions are met.
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index f6859d1..caafb42 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -27,6 +27,8 @@
 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_USB;
 import static android.os.Process.INVALID_UID;
 
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
+
 import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.ActivityManager.ForegroundServiceApiType;
@@ -218,6 +220,24 @@
         final ArrayList<Long> timestampsFound = new ArrayList<>();
         for (int i = 0, size = apiTypes.size(); i < size; i++) {
             final int apiType = apiTypes.get(i);
+
+            // remove the FGS record from the stack
+            final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
+                    uidState.mRunningFgs.get(apiType);
+            if (runningFgsOfType == null) {
+                Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
+                        + " in package " + record.packageName);
+                continue;
+            }
+
+            runningFgsOfType.remove(record.getComponentName());
+            if (runningFgsOfType.size() == 0) {
+                // there's no more FGS running for this type, just get rid of it
+                uidState.mRunningFgs.remove(apiType);
+                // but we need to keep track of the timestamp in case an API stops
+                uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
+            }
+
             final int apiTypeIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
             if (apiTypeIndex < 0) {
                 Slog.w(TAG, "Logger should be tracking FGS types correctly for UID " + uid
@@ -236,22 +256,6 @@
                 // remove the last API close call
                 uidState.mApiClosedCalls.remove(apiType);
             }
-            // remove the FGS record from the stack
-            final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
-                    uidState.mRunningFgs.get(apiType);
-            if (runningFgsOfType == null) {
-                Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
-                        + " in package " + record.packageName);
-                continue;
-            }
-
-            runningFgsOfType.remove(record.getComponentName());
-            if (runningFgsOfType.size() == 0) {
-                // there's no more FGS running for this type, just get rid of it
-                uidState.mRunningFgs.remove(apiType);
-                // but we need to keep track of the timestamp in case an API stops
-                uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
-            }
         }
         if (!apisFound.isEmpty()) {
             // time to log the call
@@ -381,9 +385,14 @@
             // initialize if we don't contain
             uidState.mOpenedWithoutFgsCount.put(apiType, 0);
         }
-        if (uidState.mOpenedWithoutFgsCount.get(apiType) != 0) {
+        int apiOpenWithoutFgsCount = uidState.mOpenedWithoutFgsCount.get(apiType);
+        if (apiOpenWithoutFgsCount != 0) {
+            apiOpenWithoutFgsCount -= 1;
+            if (apiOpenWithoutFgsCount == 0) {
+                uidState.mApiOpenCalls.remove(apiType);
+            }
             uidState.mOpenedWithoutFgsCount
-                    .put(apiType, uidState.mOpenedWithoutFgsCount.get(apiType) - 1);
+                    .put(apiType, apiOpenWithoutFgsCount);
             return System.currentTimeMillis();
         }
         // This is a part of a valid active FGS
@@ -520,7 +529,10 @@
                 r.mAllowWIUByBindings,
                 r.mAllowStartForegroundNoBinding,
                 r.mAllowStartInBindService,
-                r.mAllowStartByBindings);
+                r.mAllowStartByBindings,
+                FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                false
+        );
     }
 
     /**
@@ -578,7 +590,10 @@
                 0,
                 0,
                 0,
-                0);
+                0,
+                FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                false
+        );
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 60af280..e08fdd6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1437,6 +1437,7 @@
                 }
             });
             new MediaMetrics.Item(mMetricsId + "disconnectA2dp")
+                    .set(MediaMetrics.Property.EVENT, "disconnectA2dp")
                     .record();
             if (toRemove.size() > 0) {
                 final int delay = checkSendBecomingNoisyIntentInt(
@@ -1459,6 +1460,7 @@
                 }
             });
             new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink")
+                    .set(MediaMetrics.Property.EVENT, "disconnectA2dpSink")
                     .record();
             toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress));
         }
@@ -1474,6 +1476,7 @@
                 }
             });
             new MediaMetrics.Item(mMetricsId + "disconnectHearingAid")
+                    .set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
                     .record();
             if (toRemove.size() > 0) {
                 final int delay = checkSendBecomingNoisyIntentInt(
@@ -1531,6 +1534,7 @@
                 }
             });
             new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
+                    .set(MediaMetrics.Property.EVENT, "disconnectLeAudio")
                     .record();
             if (toRemove.size() > 0) {
                 final int delay = checkSendBecomingNoisyIntentInt(device,
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 95b6c2c..81365bf 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -719,7 +719,9 @@
     /*package*/ void initSafeMediaVolumeIndex() {
         for (int i = 0; i < mSafeMediaVolumeDevices.size(); ++i)  {
             int deviceType = mSafeMediaVolumeDevices.keyAt(i);
-            mSafeMediaVolumeDevices.put(deviceType, getSafeDeviceMediaVolumeIndex(deviceType));
+            if (mSafeMediaVolumeDevices.valueAt(i) == SAFE_MEDIA_VOLUME_UNINITIALIZED) {
+                mSafeMediaVolumeDevices.put(deviceType, getSafeDeviceMediaVolumeIndex(deviceType));
+            }
         }
     }
 
@@ -743,7 +745,7 @@
     }
 
     /*package*/ boolean safeDevicesContains(int device) {
-        return mSafeMediaVolumeDevices.indexOfKey(device) >= 0;
+        return mSafeMediaVolumeDevices.get(device, SAFE_MEDIA_VOLUME_UNINITIALIZED) >= 0;
     }
 
     /*package*/ void invalidatPendingVolumeCommand() {
@@ -1014,6 +1016,7 @@
             initCsd();
 
             synchronized (mSafeMediaVolumeStateLock) {
+                initSafeMediaVolumeIndex();
                 updateSafeMediaVolume_l(caller);
             }
         }
@@ -1065,11 +1068,18 @@
     }
 
     private int getSafeDeviceMediaVolumeIndex(int deviceType) {
-        // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
-        // instead of computing it from the volume curves
-        if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
-                || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd.get()) {
-            return mSafeMediaVolumeIndex;
+        if (!mEnableCsd.get()) {
+            // legacy hearing safety only for wired and USB HS/HP
+            if (deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
+                    || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
+                // legacy hearing safety uses mSafeMediaVolumeIndex for wired HS/HP
+                // instead of computing it from the volume curves
+                return mSafeMediaVolumeIndex;
+            }
+
+            if (deviceType != AudioSystem.DEVICE_OUT_USB_HEADSET) {
+                return SAFE_MEDIA_VOLUME_UNINITIALIZED;
+            }
         }
 
         // determine UI volume index corresponding to the wanted safe gain in dBFS
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStats.java b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
index 707240b..3c1cc00 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStats.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
@@ -16,12 +16,16 @@
 
 package com.android.server.biometrics;
 
+import android.util.Slog;
+
 /**
  * Utility class for on-device biometric authentication data, including total authentication,
  * rejections, and the number of sent enrollment notifications.
  */
 public class AuthenticationStats {
 
+    private static final String TAG = "AuthenticationStats";
+
     private static final float FRR_NOT_ENOUGH_ATTEMPTS = -1.0f;
 
     private final int mUserId;
@@ -88,6 +92,7 @@
     public void resetData() {
         mTotalAttempts = 0;
         mRejectedAttempts = 0;
+        Slog.d(TAG, "Reset Counters.");
     }
 
     /** Update enrollment notification counter after sending a notification. */
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 6edbfb7..4df2581 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -57,6 +57,7 @@
     @NonNull private final FaceManager mFaceManager;
     @NonNull private final FingerprintManager mFingerprintManager;
 
+    private final boolean mEnabled;
     private final float mThreshold;
     private final int mModality;
 
@@ -80,6 +81,7 @@
     public AuthenticationStatsCollector(@NonNull Context context, int modality,
             @NonNull BiometricNotification biometricNotification) {
         mContext = context;
+        mEnabled = context.getResources().getBoolean(R.bool.config_biometricFrrNotificationEnabled);
         mThreshold = context.getResources()
                 .getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1);
         mUserAuthenticationStatsMap = new HashMap<>();
@@ -110,6 +112,11 @@
     /** Update total authentication and rejected attempts. */
     public void authenticate(int userId, boolean authenticated) {
 
+        // Don't collect data if the feature is disabled.
+        if (!mEnabled) {
+            return;
+        }
+
         // Don't collect data for single-modality devices or user has both biometrics enrolled.
         if (isSingleModalityDevice()
                 || (hasEnrolledFace(userId) && hasEnrolledFingerprint(userId))) {
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index d4232ab..6bed42b 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -323,10 +323,15 @@
     static ProgramIdentifier identifierToHalProgramIdentifier(ProgramSelector.Identifier id) {
         ProgramIdentifier hwId = new ProgramIdentifier();
         hwId.type = id.getType();
-        if (hwId.type == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
+        if (id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
             hwId.type = IdentifierType.DAB_SID_EXT;
         }
-        hwId.value = id.getValue();
+        long value = id.getValue();
+        if (id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT) {
+            hwId.value = (value & 0xFFFF) | ((value >>> 16) << 32);
+        } else {
+            hwId.value = value;
+        }
         return hwId;
     }
 
@@ -584,6 +589,9 @@
                 || isNewIdentifierInU(info.getPhysicallyTunedTo())) {
             return false;
         }
+        if (info.getRelatedContent() == null) {
+            return true;
+        }
         Iterator<ProgramSelector.Identifier> relatedContentIt = info.getRelatedContent().iterator();
         while (relatedContentIt.hasNext()) {
             if (isNewIdentifierInU(relatedContentIt.next())) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index df16c5b..4ef2f1e 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -77,6 +77,7 @@
 import com.android.framework.protobuf.nano.MessageNano;
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.camera.flags.Flags;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -244,6 +245,7 @@
         public List<CameraStreamStats> mStreamStats;
         public String mUserTag;
         public int mVideoStabilizationMode;
+        public boolean mUsedUltraWide;
         public final long mLogId;
         public final int mSessionIndex;
 
@@ -271,7 +273,8 @@
         public void markCompleted(int internalReconfigure, long requestCount,
                 long resultErrorCount, boolean deviceError,
                 List<CameraStreamStats>  streamStats, String userTag,
-                int videoStabilizationMode, CameraExtensionSessionStats extStats) {
+                int videoStabilizationMode, boolean usedUltraWide,
+                CameraExtensionSessionStats extStats) {
             if (mCompleted) {
                 return;
             }
@@ -284,6 +287,7 @@
             mStreamStats = streamStats;
             mUserTag = userTag;
             mVideoStabilizationMode = videoStabilizationMode;
+            mUsedUltraWide = usedUltraWide;
             mExtSessionStats = extStats;
             if (CameraServiceProxy.DEBUG) {
                 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
@@ -873,6 +877,10 @@
                 streamCount = e.mStreamStats.size();
             }
             if (CameraServiceProxy.DEBUG) {
+                String ultrawideDebug = Flags.logUltrawideUsage()
+                        ? ", wideAngleUsage " + e.mUsedUltraWide
+                        : "";
+
                 Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
                         + " clientName " + e.mClientName
                         + ", duration " + e.getDuration()
@@ -889,6 +897,7 @@
                         + ", streamCount is " + streamCount
                         + ", userTag is " + e.mUserTag
                         + ", videoStabilizationMode " + e.mVideoStabilizationMode
+                        + ultrawideDebug
                         + ", logId " + e.mLogId
                         + ", sessionIndex " + e.mSessionIndex
                         + ", mExtSessionStats {type " + extensionType
@@ -952,8 +961,9 @@
                     MessageNano.toByteArray(streamProtos[2]),
                     MessageNano.toByteArray(streamProtos[3]),
                     MessageNano.toByteArray(streamProtos[4]),
-                    e.mUserTag, e.mVideoStabilizationMode, e.mLogId, e.mSessionIndex,
-                    extensionType, extensionIsAdvanced);
+                    e.mUserTag, e.mVideoStabilizationMode,
+                    e.mLogId, e.mSessionIndex,
+                    extensionType, extensionIsAdvanced, e.mUsedUltraWide);
         }
     }
 
@@ -1148,6 +1158,7 @@
         List<CameraStreamStats> streamStats = cameraState.getStreamStats();
         String userTag = cameraState.getUserTag();
         int videoStabilizationMode = cameraState.getVideoStabilizationMode();
+        boolean usedUltraWide = Flags.logUltrawideUsage() ? cameraState.getUsedUltraWide() : false;
         long logId = cameraState.getLogId();
         int sessionIdx = cameraState.getSessionIndex();
         CameraExtensionSessionStats extSessionStats = cameraState.getExtensionSessionStats();
@@ -1205,7 +1216,7 @@
                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
                         oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0,
                                 /*resultErrorCount*/0, /*deviceError*/false, streamStats,
-                                /*userTag*/"", /*videoStabilizationMode*/-1,
+                                /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false,
                                 new CameraExtensionSessionStats());
                         mCameraUsageHistory.add(oldEvent);
                     }
@@ -1217,7 +1228,7 @@
 
                         doneEvent.markCompleted(internalReconfigureCount, requestCount,
                                 resultErrorCount, deviceError, streamStats, userTag,
-                                videoStabilizationMode, extSessionStats);
+                                videoStabilizationMode, usedUltraWide, extSessionStats);
                         mCameraUsageHistory.add(doneEvent);
                         // Do not double count device error
                         deviceError = false;
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 4bfc090..21273e0 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -17,10 +17,11 @@
 package com.android.server.display;
 
 import android.hardware.display.BrightnessInfo;
+import android.os.Handler;
 import android.os.IBinder;
-import android.provider.DeviceConfigInterface;
 
-import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.brightness.clamper.HdrClamper;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.io.PrintWriter;
 import java.util.function.BooleanSupplier;
@@ -31,23 +32,30 @@
     private final NormalBrightnessModeController mNormalBrightnessModeController =
             new NormalBrightnessModeController();
 
+    private final HdrClamper mHdrClamper;
+
     private final Runnable mModeChangeCallback;
     private final boolean mUseNbmController;
 
+    private final boolean mUseHdrClamper;
+
 
     BrightnessRangeController(HighBrightnessModeController hbmController,
-            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
+            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
+            DisplayManagerFlags flags) {
         this(hbmController, modeChangeCallback, displayDeviceConfig,
-                new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
+                new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags);
     }
 
     BrightnessRangeController(HighBrightnessModeController hbmController,
             Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
-            DeviceConfigParameterProvider configParameterProvider) {
+            HdrClamper hdrClamper, DisplayManagerFlags flags) {
         mHbmController = hbmController;
         mModeChangeCallback = modeChangeCallback;
-        mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+        mUseHdrClamper = false;
+        mUseNbmController = flags.isNbmControllerEnabled();
         mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
+        mHdrClamper = hdrClamper;
     }
 
     void dump(PrintWriter pw) {
@@ -55,7 +63,6 @@
         pw.println("  mUseNormalBrightnessController=" + mUseNbmController);
         mHbmController.dump(pw);
         mNormalBrightnessModeController.dump(pw);
-
     }
 
     void onAmbientLuxChange(float ambientLux) {
@@ -63,6 +70,9 @@
                 () -> mNormalBrightnessModeController.onAmbientLuxChange(ambientLux),
                 () -> mHbmController.onAmbientLuxChange(ambientLux)
         );
+        if (mUseHdrClamper) {
+            mHdrClamper.onAmbientLuxChange(ambientLux);
+        }
     }
 
     float getNormalBrightnessMax() {
@@ -118,7 +128,8 @@
     }
 
     float getHdrBrightnessValue() {
-        return mHbmController.getHdrBrightnessValue();
+        float hdrBrightness =  mHbmController.getHdrBrightnessValue();
+        return Math.min(hdrBrightness, mHdrClamper.getMaxBrightness());
     }
 
     float getTransitionPoint() {
@@ -138,4 +149,8 @@
             hbmChangesFunc.run();
         }
     }
+
+    public float getHdrTransitionRate() {
+        return mHdrClamper.getTransitionRate();
+    }
 }
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 2d763bc..cd867f6 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -43,6 +43,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowManagerInternal;
 
 import libcore.io.Streams;
 
@@ -573,8 +574,21 @@
     }
 
     private ScreenCapture.ScreenshotHardwareBuffer captureScreen() {
-        ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
-                mDisplayManagerInternal.systemScreenshot(mDisplayId);
+        WindowManagerInternal windowManagerService = LocalServices.getService(
+                WindowManagerInternal.class);
+        ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
+        ScreenCapture.SynchronousScreenCaptureListener screenCaptureListener =
+                ScreenCapture.createSyncCaptureListener();
+        ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+                .setCaptureSecureLayers(true)
+                .setAllowProtected(true)
+                .build();
+        try {
+            windowManagerService.captureDisplay(mDisplayId, captureArgs, screenCaptureListener);
+            screenshotBuffer = screenCaptureListener.getBuffer();
+        } catch (Exception e) {
+            screenshotBuffer = null;
+        }
         if (screenshotBuffer == null) {
             Slog.e(TAG, "Failed to take screenshot. Buffer is null");
             return null;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e3dafa4..3a6e5f93 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -53,6 +53,7 @@
 import com.android.server.display.config.DisplayConfiguration;
 import com.android.server.display.config.DisplayQuirks;
 import com.android.server.display.config.HbmTiming;
+import com.android.server.display.config.HdrBrightnessData;
 import com.android.server.display.config.HighBrightnessMode;
 import com.android.server.display.config.IntegerArray;
 import com.android.server.display.config.LuxThrottling;
@@ -232,7 +233,22 @@
  *          </point>
  *        </sdrHdrRatioMap>
  *      </highBrightnessMode>
- *
+ *      <hdrBrightnessConfig>
+ *         <brightnessMap>
+ *             <point>
+ *                <first>500</first>
+ *                <second>0.3</second>
+ *             </point>
+ *             <point>
+ *                 <first>1200</first>
+ *                 <second>0.6</second>
+ *             </point>
+ *         </brightnessMap>
+ *         <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>
+ *         <brightnessIncreaseDurationMillis>10000</brightnessIncreaseDurationMillis>
+ *         <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>
+ *         <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>
+ *      </hdrBrightnessConfig>
  *      <luxThrottling>
  *        <brightnessLimitMap>
  *          <type>default</type>
@@ -769,6 +785,9 @@
     @Nullable
     private HostUsiVersion mHostUsiVersion;
 
+    @Nullable
+    private HdrBrightnessData mHdrBrightnessData;
+
     @VisibleForTesting
     DisplayDeviceConfig(Context context) {
         mContext = context;
@@ -1544,6 +1563,14 @@
     }
 
     /**
+     * @return HDR brightness related configuration
+     */
+    @Nullable
+    public HdrBrightnessData getHdrBrightnessData() {
+        return mHdrBrightnessData;
+    }
+
+    /**
      * @return Refresh rate range for specific profile id or null
      */
     @Nullable
@@ -1759,7 +1786,8 @@
                 + "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
                 mScreenOffBrightnessSensorValueToLux)
                 + "\n"
-                + "mUsiVersion= " + mHostUsiVersion
+                + "mUsiVersion= " + mHostUsiVersion + "\n"
+                + "mHdrBrightnessData" + mHdrBrightnessData
                 + "}";
     }
 
@@ -1823,6 +1851,7 @@
                 loadRefreshRateSetting(config);
                 loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
                 loadUsiVersion(config);
+                mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 90c7ce7..df45001 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -119,6 +119,7 @@
 import android.os.UserManager;
 import android.provider.DeviceConfigInterface;
 import android.provider.Settings;
+import android.sysprop.DisplayProperties;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.EventLog;
@@ -136,7 +137,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.RefreshRateRange;
 import android.window.DisplayWindowPolicyController;
-import android.window.ScreenCapture;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -489,6 +489,9 @@
 
     private boolean mBootCompleted = false;
 
+    // If we would like to keep a particular eye on a package, we can set the package name.
+    private final boolean mExtraDisplayEventLogging;
+
     private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -572,6 +575,8 @@
         mOverlayProperties = SurfaceControl.getOverlaySupport();
         mSystemReady = false;
         mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+        final String name = DisplayProperties.debug_vri_package().orElse(null);
+        mExtraDisplayEventLogging = !TextUtils.isEmpty(name);
     }
 
     public void setupSchedulerPolicies() {
@@ -2668,42 +2673,6 @@
         return null;
     }
 
-    private ScreenCapture.ScreenshotHardwareBuffer systemScreenshotInternal(int displayId) {
-        final ScreenCapture.DisplayCaptureArgs captureArgs;
-        synchronized (mSyncRoot) {
-            final IBinder token = getDisplayToken(displayId);
-            if (token == null) {
-                return null;
-            }
-            final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
-            if (logicalDisplay == null) {
-                return null;
-            }
-
-            final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
-            captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token)
-                    .setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
-                    .setCaptureSecureLayers(true)
-                    .setAllowProtected(true)
-                    .build();
-        }
-        return ScreenCapture.captureDisplay(captureArgs);
-    }
-
-    private ScreenCapture.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) {
-        synchronized (mSyncRoot) {
-            final IBinder token = getDisplayToken(displayId);
-            if (token == null) {
-                return null;
-            }
-
-            final ScreenCapture.DisplayCaptureArgs captureArgs =
-                    new ScreenCapture.DisplayCaptureArgs.Builder(token)
-                            .build();
-            return ScreenCapture.captureDisplay(captureArgs);
-        }
-    }
-
     @VisibleForTesting
     DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
             int displayId) {
@@ -2956,9 +2925,10 @@
     // Delivers display event notifications to callbacks.
     private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids,
             @DisplayEvent int event) {
-        if (DEBUG) {
+        if (DEBUG || mExtraDisplayEventLogging) {
             Slog.d(TAG, "Delivering display event: displayId="
-                    + displayId + ", event=" + event);
+                    + displayId + ", event=" + event
+                    + (uids != null ? ", uids=" + uids : ""));
         }
 
         // Grab the lock and copy the callbacks.
@@ -3267,12 +3237,12 @@
             displayPowerController = new DisplayPowerController2(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
+                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
         } else {
             displayPowerController = new DisplayPowerController(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
+                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
         }
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
         return displayPowerController;
@@ -4471,16 +4441,6 @@
         }
 
         @Override
-        public ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
-            return systemScreenshotInternal(displayId);
-        }
-
-        @Override
-        public ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId) {
-            return userScreenshotInternal(displayId);
-        }
-
-        @Override
         public DisplayInfo getDisplayInfo(int displayId) {
             return getDisplayInfoInternal(displayId, Process.myUid());
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 40dbabf..f86b8ca 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -75,6 +75,7 @@
 import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
 import com.android.server.display.utils.SensorUtils;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
@@ -592,7 +593,7 @@
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
             Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
-            boolean bootCompleted) {
+            boolean bootCompleted, DisplayManagerFlags flags) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -677,7 +678,7 @@
         HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
 
         mBrightnessRangeController = new BrightnessRangeController(hbmController,
-                modeChangeCallback, mDisplayDeviceConfig);
+                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
 
         mBrightnessThrottler = createBrightnessThrottlerLocked();
 
@@ -1921,8 +1922,25 @@
             if (isValidBrightnessValue(animateValue)
                     && (animateValue != currentBrightness
                     || sdrAnimateValue != currentSdrBrightness)) {
-                if (initialRampSkip || hasBrightnessBuckets
-                        || !isDisplayContentVisible || brightnessIsTemporary) {
+                boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
+                        || !isDisplayContentVisible || brightnessIsTemporary;
+                if (!skipAnimation && BrightnessSynchronizer.floatEquals(
+                        sdrAnimateValue, currentSdrBrightness)) {
+                    // Going from HDR to no HDR; visually this should be a "no-op" anyway
+                    // as the remaining SDR content's brightness should be holding steady
+                    // due to the sdr brightness not shifting
+                    if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
+                        skipAnimation = true;
+                    }
+
+                    // Going from no HDR to HDR; visually this is a significant scene change
+                    // and the animation just prevents advanced clients from doing their own
+                    // handling of enter/exit animations if they would like to do such a thing
+                    if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
+                        skipAnimation = true;
+                    }
+                }
+                if (skipAnimation) {
                     animateScreenBrightness(animateValue, sdrAnimateValue,
                             SCREEN_ANIMATION_RATE_MINIMUM);
                 } else {
@@ -2407,7 +2425,7 @@
             Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
                     + ", rate=" + rate);
         }
-        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
+        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, false)) {
             Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
 
             String propertyKey = "debug.tracing.screen_brightness";
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 460c351..da29981 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -77,6 +77,7 @@
 import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
 import com.android.server.display.state.DisplayStateController;
 import com.android.server.display.utils.SensorUtils;
@@ -472,7 +473,7 @@
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
             Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
-            boolean bootCompleted) {
+            boolean bootCompleted, DisplayManagerFlags flags) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -539,8 +540,8 @@
                 modeChangeCallback);
         mBrightnessThrottler = createBrightnessThrottlerLocked();
 
-        mBrightnessRangeController = new BrightnessRangeController(hbmController,
-                modeChangeCallback, mDisplayDeviceConfig);
+        mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
+                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
 
         mDisplayBrightnessController =
                 new DisplayBrightnessController(context, null,
@@ -1497,6 +1498,9 @@
             // allowed range.
             float animateValue = clampScreenBrightness(brightnessState);
 
+            // custom transition duration
+            float customTransitionRate = -1f;
+
             // If there are any HDR layers on the screen, we have a special brightness value that we
             // use instead. We still preserve the calculated brightness for Standard Dynamic Range
             // (SDR) layers, but the main brightness value will be the one for HDR.
@@ -1511,6 +1515,7 @@
                 // We want to scale HDR brightness level with the SDR level, we also need to restore
                 // SDR brightness immediately when entering dim or low power mode.
                 animateValue = mBrightnessRangeController.getHdrBrightnessValue();
+                customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
             }
 
             final float currentBrightness = mPowerState.getScreenBrightness();
@@ -1519,10 +1524,30 @@
             if (BrightnessUtils.isValidBrightnessValue(animateValue)
                     && (animateValue != currentBrightness
                     || sdrAnimateValue != currentSdrBrightness)) {
-                if (initialRampSkip || hasBrightnessBuckets
-                        || !isDisplayContentVisible || brightnessIsTemporary) {
+                boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
+                        || !isDisplayContentVisible || brightnessIsTemporary;
+                if (!skipAnimation && BrightnessSynchronizer.floatEquals(
+                        sdrAnimateValue, currentSdrBrightness)) {
+                    // Going from HDR to no HDR; visually this should be a "no-op" anyway
+                    // as the remaining SDR content's brightness should be holding steady
+                    // due to the sdr brightness not shifting
+                    if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
+                        skipAnimation = true;
+                    }
+
+                    // Going from no HDR to HDR; visually this is a significant scene change
+                    // and the animation just prevents advanced clients from doing their own
+                    // handling of enter/exit animations if they would like to do such a thing
+                    if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
+                        skipAnimation = true;
+                    }
+                }
+                if (skipAnimation) {
                     animateScreenBrightness(animateValue, sdrAnimateValue,
                             SCREEN_ANIMATION_RATE_MINIMUM);
+                } else if (customTransitionRate > 0) {
+                    animateScreenBrightness(animateValue, sdrAnimateValue,
+                            customTransitionRate, /* ignoreAnimationLimits = */true);
                 } else {
                     boolean isIncreasing = animateValue > currentBrightness;
                     final float rampSpeed;
@@ -1992,11 +2017,17 @@
     }
 
     private void animateScreenBrightness(float target, float sdrTarget, float rate) {
+        animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false);
+    }
+
+    private void animateScreenBrightness(float target, float sdrTarget, float rate,
+            boolean ignoreAnimationLimits) {
         if (DEBUG) {
             Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
                     + ", rate=" + rate);
         }
-        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
+        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate,
+                ignoreAnimationLimits)) {
             Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
 
             String propertyKey = "debug.tracing.screen_brightness";
@@ -2968,6 +2999,14 @@
                     hbmChangeCallback, hbmMetadata, context);
         }
 
+        BrightnessRangeController getBrightnessRangeController(
+                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
+                DisplayDeviceConfig displayDeviceConfig, Handler handler,
+                DisplayManagerFlags flags) {
+            return new BrightnessRangeController(hbmController,
+                    modeChangeCallback, displayDeviceConfig, handler, flags);
+        }
+
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
                 SensorManager sensorManager, Resources resources) {
             return DisplayWhiteBalanceFactory.create(handler,
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index fcaa957..9439eaa 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -6,5 +6,6 @@
 flc@google.com
 wilczynskip@google.com
 brup@google.com
+petsjonkin@google.com
 
 per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 52b92c4..5ba042c 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -30,8 +30,15 @@
     private final T mObject;
     private final FloatProperty<T> mProperty;
 
+    private final Clock mClock;
+
     private float mCurrentValue;
-    private float mTargetValue;
+
+    // target in HLG space
+    private float mTargetHlgValue;
+
+    // target in linear space
+    private float mTargetLinearValue;
     private float mRate;
     private float mAnimationIncreaseMaxTimeSecs;
     private float mAnimationDecreaseMaxTimeSecs;
@@ -42,10 +49,14 @@
 
     private boolean mFirstTime = true;
 
-
     RampAnimator(T object, FloatProperty<T> property) {
+        this(object, property, System::nanoTime);
+    }
+
+    RampAnimator(T object, FloatProperty<T> property, Clock clock) {
         mObject = object;
         mProperty = property;
+        mClock = clock;
     }
 
     /**
@@ -61,15 +72,24 @@
 
     /**
      * Sets the animation target and the rate of this ramp animator.
-     *
+     * Animation rate will be set ignoring maxTime animation limits
      * If this is the first time the property is being set or if the rate is 0,
      * the value jumps directly to the target.
      *
      * @param targetLinear The target value.
      * @param rate The convergence rate in units per second, or 0 to set the value immediately.
+     * @param ignoreAnimationLimits if mAnimationIncreaseMaxTimeSecs and
+     *                              mAnimationDecreaseMaxTimeSecs should be respected when adjusting
+     *                              animation speed
      * @return True if the target differs from the previous target.
      */
-    boolean setAnimationTarget(float targetLinear, float rate) {
+    boolean setAnimationTarget(float targetLinear, float rate, boolean ignoreAnimationLimits) {
+        float maxIncreaseTimeSecs = ignoreAnimationLimits ? 0 : mAnimationIncreaseMaxTimeSecs;
+        float maxDecreaseTimeSecs = ignoreAnimationLimits ? 0 : mAnimationDecreaseMaxTimeSecs;
+        return setAnimationTarget(targetLinear, rate, maxIncreaseTimeSecs, maxDecreaseTimeSecs);
+    }
+    private boolean setAnimationTarget(float targetLinear, float rate,
+            float maxIncreaseTimeSecs, float maxDecreaseTimeSecs) {
         // Convert the target from the linear into the HLG space.
         final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
 
@@ -78,7 +98,8 @@
             if (mFirstTime || target != mCurrentValue) {
                 mFirstTime = false;
                 mRate = 0;
-                mTargetValue = target;
+                mTargetHlgValue = target;
+                mTargetLinearValue = targetLinear;
                 mCurrentValue = target;
                 setPropertyValue(target);
                 mAnimating = false;
@@ -88,12 +109,12 @@
         }
 
         // Adjust the rate so that we do not exceed our maximum animation time.
-        if (target > mCurrentValue && mAnimationIncreaseMaxTimeSecs > 0.0f
-                && ((target - mCurrentValue) / rate) > mAnimationIncreaseMaxTimeSecs) {
-            rate = (target - mCurrentValue) / mAnimationIncreaseMaxTimeSecs;
-        } else if (target < mCurrentValue && mAnimationDecreaseMaxTimeSecs > 0.0f
-                && ((mCurrentValue - target) / rate) > mAnimationDecreaseMaxTimeSecs) {
-            rate = (mCurrentValue - target) / mAnimationDecreaseMaxTimeSecs;
+        if (target > mCurrentValue && maxIncreaseTimeSecs > 0.0f
+                && ((target - mCurrentValue) / rate) > maxIncreaseTimeSecs) {
+            rate = (target - mCurrentValue) / maxIncreaseTimeSecs;
+        } else if (target < mCurrentValue && maxDecreaseTimeSecs > 0.0f
+                && ((mCurrentValue - target) / rate) > maxDecreaseTimeSecs) {
+            rate = (mCurrentValue - target) / maxDecreaseTimeSecs;
         }
 
         // Adjust the rate based on the closest target.
@@ -105,19 +126,20 @@
         // Otherwise, continue at the previous rate.
         if (!mAnimating
                 || rate > mRate
-                || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
-                || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
+                || (target <= mCurrentValue && mCurrentValue <= mTargetHlgValue)
+                || (mTargetHlgValue <= mCurrentValue && mCurrentValue <= target)) {
             mRate = rate;
         }
 
-        final boolean changed = (mTargetValue != target);
-        mTargetValue = target;
+        final boolean changed = (mTargetHlgValue != target);
+        mTargetHlgValue = target;
+        mTargetLinearValue = targetLinear;
 
         // Start animating.
         if (!mAnimating && target != mCurrentValue) {
             mAnimating = true;
             mAnimatedValue = mCurrentValue;
-            mLastFrameTimeNanos = System.nanoTime();
+            mLastFrameTimeNanos = mClock.nanoTime();
         }
 
         return changed;
@@ -135,7 +157,11 @@
      * into linear space.
      */
     private void setPropertyValue(float val) {
-        final float linearVal = BrightnessUtils.convertGammaToLinear(val);
+        // To avoid linearVal inconsistency when converting to HLG and back to linear space
+        // used original target linear value for final animation step
+        float linearVal =
+                val == mTargetHlgValue ? mTargetLinearValue : BrightnessUtils.convertGammaToLinear(
+                        val);
         mProperty.setValue(mObject, linearVal);
     }
 
@@ -150,13 +176,13 @@
         final float scale = ValueAnimator.getDurationScale();
         if (scale == 0) {
             // Animation off.
-            mAnimatedValue = mTargetValue;
+            mAnimatedValue = mTargetHlgValue;
         } else {
             final float amount = timeDelta * mRate / scale;
-            if (mTargetValue > mCurrentValue) {
-                mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
+            if (mTargetHlgValue > mCurrentValue) {
+                mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetHlgValue);
             } else {
-                mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
+                mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetHlgValue);
             }
         }
         final float oldCurrentValue = mCurrentValue;
@@ -164,7 +190,7 @@
         if (oldCurrentValue != mCurrentValue) {
             setPropertyValue(mCurrentValue);
         }
-        if (mTargetValue == mCurrentValue) {
+        if (mTargetHlgValue == mCurrentValue) {
             mAnimating = false;
         }
     }
@@ -173,6 +199,13 @@
         void onAnimationEnd();
     }
 
+    interface Clock {
+        /**
+         * Returns current system time in nanoseconds.
+         */
+        long nanoTime();
+    }
+
     static class DualRampAnimator<T> {
         private final Choreographer mChoreographer;
         private final RampAnimator<T> mFirst;
@@ -208,11 +241,17 @@
          * @param linearFirstTarget The first target value in linear space.
          * @param linearSecondTarget The second target value in linear space.
          * @param rate The convergence rate in units per second, or 0 to set the value immediately.
+         * @param ignoreAnimationLimits if mAnimationIncreaseMaxTimeSecs and
+         *                              mAnimationDecreaseMaxTimeSecs should be respected
+         *                              when adjusting animation speed
          * @return True if either target differs from the previous target.
          */
-        public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
-            boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate);
-            animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate);
+        public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate,
+                boolean ignoreAnimationLimits) {
+            boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate,
+                    ignoreAnimationLimits);
+            animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate,
+                    ignoreAnimationLimits);
             boolean shouldBeAnimating = isAnimating();
 
             if (shouldBeAnimating != mAwaitingCallback) {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6936112..d910e16 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -167,6 +167,8 @@
             int width, int height, int densityDpi) {
         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
         if (device != null) {
+            Slog.v(TAG, "Resize VirtualDisplay " + device.mName + " to " + width
+                    + " " + height);
             device.resizeLocked(width, height, densityDpi);
         }
     }
@@ -183,6 +185,7 @@
     public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
         if (device != null) {
+            Slog.v(TAG, "Update surface for VirtualDisplay " + device.mName);
             device.setSurfaceLocked(surface);
         }
     }
@@ -197,6 +200,7 @@
     public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
         VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
         if (device != null) {
+            Slog.v(TAG, "Release VirtualDisplay " + device.mName);
             device.destroyLocked(true);
             appToken.unlinkToDeath(device, 0);
         }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
new file mode 100644
index 0000000..079a196
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -0,0 +1,132 @@
+/*
+ * 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.display.brightness.clamper;
+
+import android.os.Handler;
+import android.os.PowerManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class HdrClamper {
+
+    private final Configuration mConfiguration = new Configuration();
+
+    private final BrightnessClamperController.ClamperChangeListener mClamperChangeListener;
+
+    private final Handler mHandler;
+
+    private final Runnable mDebouncer;
+
+    private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+
+    // brightness change speed, in units per seconds,
+    private float mTransitionRate = -1f;
+
+    private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+
+    private float mDesiredTransitionDuration = -1; // in seconds
+
+    public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+            Handler handler) {
+        mClamperChangeListener = clamperChangeListener;
+        mHandler = handler;
+        mDebouncer = () -> {
+            mTransitionRate = Math.abs((mMaxBrightness - mDesiredMaxBrightness)
+                    / mDesiredTransitionDuration);
+            mMaxBrightness = mDesiredMaxBrightness;
+            mClamperChangeListener.onChanged();
+        };
+    }
+
+    // Called in same looper: mHandler.getLooper()
+    public float getMaxBrightness() {
+        return mMaxBrightness;
+    }
+
+    // Called in same looper: mHandler.getLooper()
+    public float getTransitionRate() {
+        return mTransitionRate;
+    }
+
+
+    /**
+     * Updates brightness cap in response to ambient lux change.
+     * Called by ABC in same looper: mHandler.getLooper()
+     */
+    public void onAmbientLuxChange(float ambientLux) {
+        float expectedMaxBrightness = findBrightnessLimit(ambientLux);
+        if (mMaxBrightness == expectedMaxBrightness) {
+            mDesiredMaxBrightness = mMaxBrightness;
+            mDesiredTransitionDuration = -1;
+            mTransitionRate = -1f;
+            mHandler.removeCallbacks(mDebouncer);
+        } else if (mDesiredMaxBrightness != expectedMaxBrightness) {
+            mDesiredMaxBrightness = expectedMaxBrightness;
+            long debounceTime;
+            if (mDesiredMaxBrightness > mMaxBrightness) {
+                debounceTime = mConfiguration.mIncreaseConfig.mDebounceTimeMillis;
+                mDesiredTransitionDuration =
+                        (float) mConfiguration.mIncreaseConfig.mTransitionTimeMillis / 1000;
+            } else {
+                debounceTime = mConfiguration.mDecreaseConfig.mDebounceTimeMillis;
+                mDesiredTransitionDuration =
+                        (float) mConfiguration.mDecreaseConfig.mTransitionTimeMillis / 1000;
+            }
+
+            mHandler.removeCallbacks(mDebouncer);
+            mHandler.postDelayed(mDebouncer, debounceTime);
+        }
+    }
+
+    @VisibleForTesting
+    Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    private float findBrightnessLimit(float ambientLux) {
+        float foundAmbientBoundary = Float.MAX_VALUE;
+        float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+        for (Map.Entry<Float, Float> brightnessPoint :
+                mConfiguration.mMaxBrightnessLimits.entrySet()) {
+            float ambientBoundary = brightnessPoint.getKey();
+            // find ambient lux upper boundary closest to current ambient lux
+            if (ambientBoundary > ambientLux && ambientBoundary < foundAmbientBoundary) {
+                foundMaxBrightness = brightnessPoint.getValue();
+                foundAmbientBoundary = ambientBoundary;
+            }
+        }
+        return foundMaxBrightness;
+    }
+
+    @VisibleForTesting
+    static class Configuration {
+        final Map<Float, Float> mMaxBrightnessLimits = new HashMap<>();
+        final TransitionConfiguration mIncreaseConfig = new TransitionConfiguration();
+
+        final TransitionConfiguration mDecreaseConfig = new TransitionConfiguration();
+    }
+
+    @VisibleForTesting
+    static class TransitionConfiguration {
+        long mDebounceTimeMillis;
+
+        long mTransitionTimeMillis;
+    }
+}
diff --git a/services/core/java/com/android/server/display/config/HdrBrightnessData.java b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
new file mode 100644
index 0000000..06d3c5b
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
@@ -0,0 +1,98 @@
+/*
+ * 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.display.config;
+
+import android.annotation.Nullable;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Brightness config for HDR content
+ */
+public class HdrBrightnessData {
+
+    /**
+     * Lux to brightness map
+     */
+    public final Map<Float, Float> mMaxBrightnessLimits;
+
+    /**
+     * Debounce time for brightness increase
+     */
+    public final long mBrightnessIncreaseDebounceMillis;
+
+    /**
+     * Brightness increase animation duration
+     */
+    public final long mBrightnessIncreaseDurationMillis;
+
+    /**
+     * Debounce time for brightness decrease
+     */
+    public final long mBrightnessDecreaseDebounceMillis;
+
+    /**
+     * Brightness decrease animation duration
+     */
+    public final long mBrightnessDecreaseDurationMillis;
+
+    private HdrBrightnessData(Map<Float, Float> maxBrightnessLimits,
+            long brightnessIncreaseDebounceMillis, long brightnessIncreaseDurationMillis,
+            long brightnessDecreaseDebounceMillis, long brightnessDecreaseDurationMillis) {
+        mMaxBrightnessLimits = maxBrightnessLimits;
+        mBrightnessIncreaseDebounceMillis = brightnessIncreaseDebounceMillis;
+        mBrightnessIncreaseDurationMillis = brightnessIncreaseDurationMillis;
+        mBrightnessDecreaseDebounceMillis = brightnessDecreaseDebounceMillis;
+        mBrightnessDecreaseDurationMillis = brightnessDecreaseDurationMillis;
+    }
+
+    @Override
+    public String toString() {
+        return "HdrBrightnessData {"
+                + "mMaxBrightnessLimits: " + mMaxBrightnessLimits
+                + ", mBrightnessIncreaseDebounceMillis: " + mBrightnessIncreaseDebounceMillis
+                + ", mBrightnessIncreaseDurationMillis: " + mBrightnessIncreaseDurationMillis
+                + ", mBrightnessDecreaseDebounceMillis: " + mBrightnessDecreaseDebounceMillis
+                + ", mBrightnessDecreaseDurationMillis: " + mBrightnessDecreaseDurationMillis
+                + "} ";
+    }
+
+    /**
+     * Loads HdrBrightnessData from DisplayConfiguration
+     */
+    @Nullable
+    public static HdrBrightnessData loadConfig(DisplayConfiguration config) {
+        HdrBrightnessConfig hdrConfig = config.getHdrBrightnessConfig();
+        if (hdrConfig == null) {
+            return null;
+        }
+
+        List<NonNegativeFloatToFloatPoint> points = hdrConfig.getBrightnessMap().getPoint();
+        Map<Float, Float> brightnessLimits = new HashMap<>();
+        for (NonNegativeFloatToFloatPoint point: points) {
+            brightnessLimits.put(point.getFirst().floatValue(), point.getSecond().floatValue());
+        }
+
+        return new HdrBrightnessData(brightnessLimits,
+                hdrConfig.getBrightnessIncreaseDebounceMillis().longValue(),
+                hdrConfig.getBrightnessIncreaseDurationMillis().longValue(),
+                hdrConfig.getBrightnessDecreaseDebounceMillis().longValue(),
+                hdrConfig.getBrightnessDecreaseDurationMillis().longValue());
+    }
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index fddac6d..aebd8a0 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -30,43 +30,68 @@
 public class DisplayManagerFlags {
     private static final boolean DEBUG = false;
     private static final String TAG = "DisplayManagerFlags";
-    private boolean mIsConnectedDisplayManagementEnabled = false;
-    private boolean mIsConnectedDisplayManagementEnabledSet = false;
 
-    private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
-        // TODO(b/299462337) Remove when the infrastructure is ready.
-        if ((Build.IS_ENG || Build.IS_USERDEBUG)
-                    && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
-            return true;
-        }
-        try {
-            return flagFunction.get();
-        } catch (Throwable ex) {
-            if (DEBUG) {
-                Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
-            }
-            return false;
-        }
-    }
+    private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
+            Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
+            Flags::enableConnectedDisplayManagement);
 
-    // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+    private final FlagState mNbmControllerFlagState = new FlagState(
+            Flags.FLAG_ENABLE_NBM_CONTROLLER,
+            Flags::enableNbmController);
+
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
-        if (mIsConnectedDisplayManagementEnabledSet) {
-            if (DEBUG) {
-                Slog.d(TAG, "isConnectedDisplayManagementEnabled. Recall = "
-                                    + mIsConnectedDisplayManagementEnabled);
+        return mConnectedDisplayManagementFlagState.isEnabled();
+    }
+
+    /** Returns whether hdr clamper is enabled on not*/
+    public boolean isNbmControllerEnabled() {
+        return mNbmControllerFlagState.isEnabled();
+    }
+
+    private static class FlagState {
+
+        private final String mName;
+
+        private final Supplier<Boolean> mFlagFunction;
+        private boolean mEnabledSet;
+        private boolean mEnabled;
+
+        private FlagState(String name, Supplier<Boolean> flagFunction) {
+            mName = name;
+            mFlagFunction = flagFunction;
+        }
+
+        // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+        private boolean isEnabled() {
+            if (mEnabledSet) {
+                if (DEBUG) {
+                    Slog.d(TAG, mName + ": mEnabled. Recall = " + mEnabled);
+                }
+                return mEnabled;
             }
-            return mIsConnectedDisplayManagementEnabled;
+            mEnabled = flagOrSystemProperty(mFlagFunction, mName);
+            if (DEBUG) {
+                Slog.d(TAG, mName + ": mEnabled. Flag value = " + mEnabled);
+            }
+            mEnabledSet = true;
+            return mEnabled;
         }
-        mIsConnectedDisplayManagementEnabled =
-                flagOrSystemProperty(Flags::enableConnectedDisplayManagement,
-                        Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT);
-        if (DEBUG) {
-            Slog.d(TAG, "isConnectedDisplayManagementEnabled. Flag value = "
-                                + mIsConnectedDisplayManagementEnabled);
+
+        private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
+            // TODO(b/299462337) Remove when the infrastructure is ready.
+            if ((Build.IS_ENG || Build.IS_USERDEBUG)
+                    && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
+                return true;
+            }
+            try {
+                return flagFunction.get();
+            } catch (Throwable ex) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
+                }
+                return false;
+            }
         }
-        mIsConnectedDisplayManagementEnabledSet = true;
-        return mIsConnectedDisplayManagementEnabled;
     }
 }
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 2c3c66e..12306b0 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -9,3 +9,11 @@
     bug: "280739508"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "enable_nbm_controller"
+    namespace: "display_manager"
+    description: "Feature flag for Normal Brightness Mode Controller"
+    bug: "277877297"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index a2c8748..2ede56d 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -62,10 +62,10 @@
         mWindowHandle.ownerUid = uid;
         mWindowHandle.scaleFactor = 1.0f;
         mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
-        mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY;
+        mWindowHandle.inputConfig =
+                InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY;
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
         t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 62660c4..6b399de 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -95,6 +95,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.PointerIcon;
 import android.view.Surface;
@@ -682,6 +683,12 @@
         return mNative.getKeyCodeForKeyLocation(deviceId, locationKeyCode);
     }
 
+    @Override // Binder call
+    public KeyCharacterMap getKeyCharacterMap(@NonNull String layoutDescriptor) {
+        Objects.requireNonNull(layoutDescriptor, "layoutDescriptor must not be null");
+        return mKeyboardLayoutManager.getKeyCharacterMap(layoutDescriptor);
+    }
+
     /**
      * Transfer the current touch gesture to the provided window.
      *
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index a5162c0..0eb620f 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -63,6 +63,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InputDevice;
+import android.view.KeyCharacterMap;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
@@ -430,6 +431,23 @@
         return result[0];
     }
 
+    @AnyThread
+    public KeyCharacterMap getKeyCharacterMap(@NonNull String layoutDescriptor) {
+        final String[] overlay = new String[1];
+        visitKeyboardLayout(layoutDescriptor,
+                (resources, keyboardLayoutResId, layout) -> {
+                    try (InputStreamReader stream = new InputStreamReader(
+                            resources.openRawResource(keyboardLayoutResId))) {
+                        overlay[0] = Streams.readFully(stream);
+                    } catch (IOException | Resources.NotFoundException ignored) {
+                    }
+                });
+        if (TextUtils.isEmpty(overlay[0])) {
+            return KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+        }
+        return KeyCharacterMap.load(layoutDescriptor, overlay[0]);
+    }
+
     private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
         final PackageManager pm = mContext.getPackageManager();
         Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index dbbbed3..7726f40 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -57,13 +57,13 @@
                 InputConfig.NOT_FOCUSABLE
                         | InputConfig.NOT_TOUCHABLE
                         | InputConfig.SPY
-                        | InputConfig.INTERCEPTS_STYLUS;
+                        | InputConfig.INTERCEPTS_STYLUS
+                        | InputConfig.TRUSTED_OVERLAY;
 
         // Configure the surface to receive stylus events across the entire display.
         mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
         t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 699e9c8..032778c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -116,7 +116,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
-import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -124,7 +123,6 @@
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.WindowManagerGlobal;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputBinding;
@@ -2989,9 +2987,13 @@
             ConcurrentUtils.waitForFutureNoInterrupt(mImeDrawsImeNavBarResLazyInitFuture,
                     "Waiting for the lazy init of mImeDrawsImeNavBarRes");
         }
+        // Whether the current display has a navigation bar. When this is false (e.g. emulator),
+        // the IME should not draw the IME navigation bar.
+        final boolean hasNavigationBar = mWindowManagerInternal
+                .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
+                        ? mCurTokenDisplayId : DEFAULT_DISPLAY);
         final boolean canImeDrawsImeNavBar =
-                mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get()
-                        && hasNavigationBarOnCurrentDisplay();
+                mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
         final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
                 InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
         return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
@@ -2999,21 +3001,6 @@
                 ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
     }
 
-    /**
-     * Whether the current display has a navigation bar. When this is {@code false} (e.g. emulator),
-     * the IME should <em>not</em> draw the IME navigation bar.
-     */
-    @GuardedBy("ImfLock.class")
-    private boolean hasNavigationBarOnCurrentDisplay() {
-        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-        try {
-            return wm.hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
-                    ? mCurTokenDisplayId : DEFAULT_DISPLAY);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     @GuardedBy("ImfLock.class")
     private boolean shouldShowImeSwitcherLocked(int visibility) {
         if (!mShowOngoingImeSwitcherForPhones) return false;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 0cfdaf2..10b6052 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -195,8 +195,12 @@
         mApplicationKeyStorage = applicationKeyStorage;
         mTestCertHelper = testOnlyInsecureCertificateHelper;
         mCleanupManager = cleanupManager;
-        // Clears data for removed users.
-        mCleanupManager.verifyKnownUsers();
+        try {
+            // Clears data for removed users.
+            mCleanupManager.verifyKnownUsers();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to verify known users", e);
+        }
         try {
             mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
         } catch (NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java
index 66985e0..ddeeacc 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteController.java
@@ -24,6 +24,8 @@
 import android.media.MediaRoute2Info;
 import android.os.UserHandle;
 
+import com.android.media.flags.Flags;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -53,15 +55,10 @@
             return new NoOpBluetoothRouteController();
         }
 
-        MediaFeatureFlagManager flagManager = MediaFeatureFlagManager.getInstance();
-        boolean isUsingLegacyController = flagManager.getBoolean(
-                MediaFeatureFlagManager.FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER,
-                true);
-
-        if (isUsingLegacyController) {
-            return new LegacyBluetoothRouteController(context, btAdapter, listener);
-        } else {
+        if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
             return new AudioPoliciesBluetoothRouteController(context, btAdapter, listener);
+        } else {
+            return new LegacyBluetoothRouteController(context, btAdapter, listener);
         }
     }
 
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 3875c84..e17f4a3 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -25,6 +25,8 @@
 import android.media.MediaRoute2Info;
 import android.os.ServiceManager;
 
+import com.android.media.flags.Flags;
+
 /**
  * Controls device routes.
  *
@@ -44,18 +46,13 @@
         IAudioService audioService = IAudioService.Stub.asInterface(
                 ServiceManager.getService(Context.AUDIO_SERVICE));
 
-        MediaFeatureFlagManager flagManager = MediaFeatureFlagManager.getInstance();
-        boolean isUsingLegacyController = flagManager.getBoolean(
-                MediaFeatureFlagManager.FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER,
-                true);
-
-        if (isUsingLegacyController) {
-            return new LegacyDeviceRouteController(context,
+        if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+            return new AudioPoliciesDeviceRouteController(context,
                     audioManager,
                     audioService,
                     onDeviceRouteChangedListener);
         } else {
-            return new AudioPoliciesDeviceRouteController(context,
+            return new LegacyDeviceRouteController(context,
                     audioManager,
                     audioService,
                     onDeviceRouteChangedListener);
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index e31a7fc..1980403 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -508,7 +508,11 @@
                 case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
                     clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
                     if (device != null) {
-                        addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
+                        if (DEBUG) {
+                            Log.d(TAG, "Setting active a2dp devices. device=" + device);
+                        }
+
+                        addActiveDevices(device);
                     }
                     notifyBluetoothRoutesUpdated();
                     break;
diff --git a/services/core/java/com/android/server/media/MediaFeatureFlagManager.java b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
index f555505..f90f64a 100644
--- a/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
+++ b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
@@ -36,7 +36,6 @@
     @StringDef(
             prefix = "FEATURE_",
             value = {
-                FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER,
                 FEATURE_SCANNING_MINIMUM_PACKAGE_IMPORTANCE
             })
     @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@@ -44,14 +43,6 @@
     /* package */ @interface MediaFeatureFlag {}
 
     /**
-     * Whether to use old legacy implementation of BluetoothRouteController or new
-     * 'Audio Strategies'-aware controller.
-     */
-    /* package */ static final @MediaFeatureFlag String
-            FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER =
-            "BluetoothRouteController__enable_legacy_bluetooth_routes_controller";
-
-    /**
      * Whether to use IMPORTANCE_FOREGROUND (i.e. 100) or IMPORTANCE_FOREGROUND_SERVICE (i.e. 125)
      * as the minimum package importance for scanning.
      */
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 69e4a5b..13d1662 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -932,6 +932,7 @@
             if (callback == null) {
                 throw new IllegalArgumentException("callback must not be null");
             }
+            Slog.v(TAG, "Start the token instance " + this);
             // Cache result of calling into ActivityManagerService outside of the lock, to prevent
             // deadlock with WindowManagerService.
             final boolean hasFGS = mActivityManagerInternal.hasRunningForegroundService(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6a5b9d8..bada816 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2826,9 +2826,6 @@
             mAssistants.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
             mHistoryManager.onBootPhaseAppsCanStart();
-            if (expireBitmaps()) {
-                NotificationBitmapJobService.scheduleJob(getContext());
-            }
             registerDeviceConfigChange();
             migrateDefaultNAS();
             maybeShowInitialReviewPermissionsNotification();
@@ -2837,6 +2834,10 @@
         } else if (phase == SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY) {
             mPreferencesHelper.updateFixedImportance(mUm.getUsers());
             mPreferencesHelper.migrateNotificationPermissions(mUm.getUsers());
+        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+            if (expireBitmaps()) {
+                NotificationBitmapJobService.scheduleJob(getContext());
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index b015a72..d2e980b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -39,6 +39,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.FrameworkStatsLog;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -497,6 +498,7 @@
         final boolean is_non_dismissible;
         final int fsi_state;
         final boolean is_locked;
+        final int age_in_minutes;
         @DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
 
         NotificationReported(NotificationRecordPair p,
@@ -541,6 +543,9 @@
                     hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
 
             this.is_locked = p.r.isLocked();
+
+            this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
+                    p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().when);
         }
     }
 
@@ -601,4 +606,13 @@
         }
         return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI;
     }
+
+    /**
+     * @param postTimeMs time (in {@link System#currentTimeMillis} time) the notification was posted
+     * @param whenMs A timestamp related to this notification, in milliseconds since the epoch.
+     * @return difference in duration as an integer in minutes
+     */
+    static int getAgeInMinutes(long postTimeMs, long whenMs) {
+        return (int) Duration.ofMillis(postTimeMs - whenMs).toMinutes();
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9da0e98..fc0a776 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -77,7 +77,8 @@
                 notificationReported.is_non_dismissible,
                 notificationReported.post_duration_millis,
                 notificationReported.fsi_state,
-                notificationReported.is_locked);
+                notificationReported.is_locked,
+                notificationReported.age_in_minutes);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 85c140c..e14f7c0 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -16,7 +16,6 @@
 
 package com.android.server.notification;
 
-import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -203,7 +202,6 @@
                         Context.DEVICE_ID_DEFAULT, userId, TAG);
             }
             int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
-            flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask;
             if (userSet) {
                 mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, flagMask,
                         FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, userId);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a700d32..71562dc 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1022,6 +1022,8 @@
     @VisibleForTesting
     protected void setZenModeSetting(int zen) {
         Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
+        ZenLog.traceSetZenMode(Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, -1),
+                "updated setting");
         showZenUpgradeNotification(zen);
     }
 
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index f95f7bc..bd9be30 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -17,7 +17,6 @@
 package com.android.server.pm;
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
@@ -228,7 +227,7 @@
                 userId, flags, appId, seInfo, targetSdkVersion, usesSdk);
         args.previousAppId = previousAppId;
 
-        return batch.createAppData(args).whenComplete((ceDataInode, e) -> {
+        return batch.createAppData(args).whenComplete((createAppDataResult, e) -> {
             // Note: this code block is executed with the Installer lock
             // already held, since it's invoked as a side-effect of
             // executeBatchLI()
@@ -237,7 +236,7 @@
                         + ", but trying to recover: " + e);
                 destroyAppDataLeafLIF(pkg, userId, flags);
                 try {
-                    ceDataInode = mInstaller.createAppData(args).ceDataInode;
+                    createAppDataResult = mInstaller.createAppData(args);
                     logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
                 } catch (Installer.InstallerException e2) {
                     logCriticalInfo(Log.DEBUG, "Recovery failed!");
@@ -279,12 +278,19 @@
                 }
             }
 
+            final long ceDataInode = createAppDataResult.ceDataInode;
+            final long deDataInode = createAppDataResult.deDataInode;
+
             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
-                // TODO: mark this structure as dirty so we persist it!
                 synchronized (mPm.mLock) {
                     ps.setCeDataInode(ceDataInode, userId);
                 }
             }
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && deDataInode != -1) {
+                synchronized (mPm.mLock) {
+                    ps.setDeDataInode(deDataInode, userId);
+                }
+            }
 
             prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
         });
@@ -609,7 +615,7 @@
         destroyAppDataLeafLIF(pkg, userId, flags);
     }
 
-    public void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
+    private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
         final Computer snapshot = mPm.snapshotComputer();
         final PackageStateInternal packageStateInternal =
                 snapshot.getPackageStateInternal(pkg.getPackageName());
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index ffa2af1..316c4ac 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1024,7 +1024,7 @@
         if ("android".equals(packageName) || "system".equals(packageName)) {
             return androidApplication();
         }
-        if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
+        if ((flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
             // Already generates the external package name
             return generateApplicationInfoFromSettings(packageName,
                     flags, filterCallingUid, userId);
@@ -1518,7 +1518,6 @@
             pi.sharedUserId = (sharedUser != null) ? sharedUser.getName() : null;
             pi.firstInstallTime = state.getFirstInstallTimeMillis();
             pi.lastUpdateTime = ps.getLastUpdateTime();
-            pi.isArchived = isArchived(state);
 
             ApplicationInfo ai = new ApplicationInfo();
             ai.packageName = ps.getPackageName();
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b5a373e..83f90a1 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -24,6 +24,7 @@
 import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
 import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
@@ -52,6 +53,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -119,8 +121,6 @@
      */
     public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
             boolean removedBySystem) {
-        final boolean isArchived = false; // TODO(b/278553670) Pass true during archival.
-
         final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
         final boolean res;
 
@@ -250,6 +250,7 @@
 
         if (res) {
             final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
+            final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0;
             info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived);
             info.sendSystemPackageUpdatedBroadcasts();
             PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser);
@@ -561,6 +562,17 @@
                 Slog.d(TAG, "Marking package:" + ps.getPackageName()
                         + " uninstalled for user:" + nextUserId);
             }
+
+            // Keep enabled and disabled components in case of DELETE_KEEP_DATA
+            ArraySet<String> enabledComponents = null;
+            ArraySet<String> disabledComponents = null;
+            if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
+                enabledComponents = new ArraySet<String>(
+                        ps.readUserState(nextUserId).getEnabledComponents());
+                disabledComponents = new ArraySet<String>(
+                        ps.readUserState(nextUserId).getDisabledComponents());
+            }
+
             // Preserve ArchiveState if this is not a full uninstall
             ArchiveState archiveState =
                     (flags & DELETE_KEEP_DATA) == 0
@@ -569,6 +581,7 @@
 
             ps.setUserState(nextUserId,
                     ps.getCeDataInode(nextUserId),
+                    ps.getDeDataInode(nextUserId),
                     COMPONENT_ENABLED_STATE_DEFAULT,
                     false /*installed*/,
                     true /*stopped*/,
@@ -579,8 +592,8 @@
                     false /*instantApp*/,
                     false /*virtualPreload*/,
                     null /*lastDisableAppCaller*/,
-                    null /*enabledComponents*/,
-                    null /*disabledComponents*/,
+                    enabledComponents,
+                    disabledComponents,
                     PackageManager.INSTALL_REASON_UNKNOWN,
                     PackageManager.UNINSTALL_REASON_UNKNOWN,
                     null /*harmfulAppWarning*/,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 468b3a7..e1e5e6d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -44,6 +44,7 @@
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
 import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -56,7 +57,6 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
 import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
-import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
 import static com.android.server.pm.PackageManagerService.MIN_INSTALLABLE_TARGET_SDK;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.pm.PackageManagerService.POST_INSTALL;
@@ -112,6 +112,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.DataLoaderType;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
@@ -180,7 +181,6 @@
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedLibraryWrapper;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
@@ -1144,15 +1144,18 @@
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
         final ParsedPackage parsedPackage;
+        final ArchivedPackageParcel archivedPackage;
         try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
             if (request.getPackageLite() == null || !request.isArchived()) {
                 // TODO: pass packageLite from install request instead of reparsing the package
                 parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
                 AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+                archivedPackage = null;
             } else {
                 // Archived install mode, no APK.
                 parsedPackage = pp.parsePackageFromPackageLite(request.getPackageLite(),
                         parseFlags);
+                archivedPackage = request.getPackageLite().getArchivedPackage();
             }
         } catch (PackageManagerException e) {
             throw new PrepareFailure("Failed parse during installPackageLI", e);
@@ -1245,6 +1248,7 @@
         boolean systemApp = false;
         boolean replace = false;
         synchronized (mPm.mLock) {
+            final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
             // Check if installing already existing package
             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                 String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName);
@@ -1261,16 +1265,15 @@
                         Slog.d(TAG, "Replacing existing renamed package: oldName="
                                 + oldName + " pkgName=" + pkgName);
                     }
-                } else if (mPm.mPackages.containsKey(pkgName)) {
+                } else if (ps != null) {
                     // This package, under its official name, already exists
                     // on the device; we should replace it.
                     replace = true;
                     if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName);
                 }
-
-                if (replace) {
+                final AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
+                if (replace && oldPackage != null) {
                     // Prevent apps opting out from runtime permissions
-                    AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
                     final int oldTargetSdk = oldPackage.getTargetSdkVersion();
                     final int newTargetSdk = parsedPackage.getTargetSdkVersion();
                     if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
@@ -1292,7 +1295,6 @@
                 }
             }
 
-            PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
             PackageSetting signatureCheckPs = ps;
 
             // SDK libs can have other major versions with different package names.
@@ -1368,8 +1370,8 @@
                 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
 
                 systemApp = ps.isSystem();
-                request.setOriginUsers(
-                        ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true));
+                request.setOriginUsers(ps.queryUsersInstalledOrHasData(
+                        mPm.mUserManager.getUserIds()));
             }
 
             final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
@@ -1595,7 +1597,7 @@
 
         boolean shouldCloseFreezerBeforeReturn = true;
         try {
-            final PackageState oldPackageState;
+            final PackageSetting oldPackageState;
             final AndroidPackage oldPackage;
             String renamedPackage;
             boolean sysPkg = false;
@@ -1648,7 +1650,7 @@
                         }
                     } else {
                         SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
-                        SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
+                        SigningDetails oldPkgSigningDetails = oldPackageState.getSigningDetails();
                         // default to original signature matching
                         if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
                                 SigningDetails.CertCapabilities.INSTALLED_DATA)
@@ -1668,7 +1670,8 @@
                     }
 
                     // don't allow a system upgrade unless the upgrade hash matches
-                    if (oldPackage.getRestrictUpdateHash() != null && oldPackageState.isSystem()) {
+                    if (oldPackage != null && oldPackage.getRestrictUpdateHash() != null
+                            && oldPackageState.isSystem()) {
                         final byte[] digestBytes;
                         try {
                             final MessageDigest digest = MessageDigest.getInstance("SHA-512");
@@ -1691,23 +1694,26 @@
                         parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
                     }
 
-                    // APK should not change its sharedUserId declarations
-                    final var oldSharedUid = oldPackage.getSharedUserId() != null
-                            ? oldPackage.getSharedUserId() : "<nothing>";
-                    final var newSharedUid = parsedPackage.getSharedUserId() != null
-                            ? parsedPackage.getSharedUserId() : "<nothing>";
-                    if (!oldSharedUid.equals(newSharedUid)) {
-                        throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
-                                "Package " + parsedPackage.getPackageName()
-                                        + " shared user changed from "
-                                        + oldSharedUid + " to " + newSharedUid);
-                    }
+                    if (oldPackage != null) {
+                        // APK should not change its sharedUserId declarations
+                        final var oldSharedUid = oldPackage.getSharedUserId() != null
+                                ? oldPackage.getSharedUserId() : "<nothing>";
+                        final var newSharedUid = parsedPackage.getSharedUserId() != null
+                                ? parsedPackage.getSharedUserId() : "<nothing>";
+                        if (!oldSharedUid.equals(newSharedUid)) {
+                            throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
+                                    "Package " + parsedPackage.getPackageName()
+                                            + " shared user changed from "
+                                            + oldSharedUid + " to " + newSharedUid);
+                        }
 
-                    // APK should not re-join shared UID
-                    if (oldPackage.isLeavingSharedUser() && !parsedPackage.isLeavingSharedUser()) {
-                        throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
-                                "Package " + parsedPackage.getPackageName()
-                                        + " attempting to rejoin " + newSharedUid);
+                        // APK should not re-join shared UID
+                        if (oldPackage.isLeavingSharedUser()
+                                && !parsedPackage.isLeavingSharedUser()) {
+                            throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
+                                    "Package " + parsedPackage.getPackageName()
+                                            + " attempting to rejoin " + newSharedUid);
+                        }
                     }
 
                     // In case of rollback, remember per-user/profile install state
@@ -1740,8 +1746,8 @@
 
                 // Update what is removed
                 PackageRemovedInfo removedInfo = new PackageRemovedInfo(mPm);
-                removedInfo.mUid = oldPackage.getUid();
-                removedInfo.mRemovedPackage = oldPackage.getPackageName();
+                removedInfo.mUid = ps.getAppId();
+                removedInfo.mRemovedPackage = ps.getPackageName();
                 removedInfo.mInstallerPackageName =
                         ps.getInstallSource().mInstallerPackageName;
                 removedInfo.mIsStaticSharedLib =
@@ -1760,8 +1766,8 @@
                     removedInfo.mUninstallReasons.put(userId,
                             ps.getUninstallReason(userId));
                 }
-                removedInfo.mIsExternal = oldPackage.isExternalStorage();
-                removedInfo.mRemovedPackageVersionCode = oldPackage.getLongVersionCode();
+                removedInfo.mIsExternal = oldPackageState.isExternalStorage();
+                removedInfo.mRemovedPackageVersionCode = oldPackageState.getVersionCode();
                 request.setRemovedInfo(removedInfo);
 
                 sysPkg = oldPackageState.isSystem();
@@ -1801,7 +1807,7 @@
             } else { // new package install
                 ps = null;
                 disabledPs = null;
-                oldPackage = null;
+                oldPackageState = null;
                 // Remember this for later, in case we need to rollback this install
                 String pkgName1 = parsedPackage.getPackageName();
 
@@ -1832,8 +1838,8 @@
             shouldCloseFreezerBeforeReturn = false;
 
             request.setPrepareResult(replace, targetScanFlags, targetParseFlags,
-                    oldPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
-                    ps, disabledPs);
+                    oldPackageState, parsedPackage, archivedPackage,
+                    replace /* clearCodeCache */, sysPkg, ps, disabledPs);
         } finally {
             request.setFreezer(freezer);
             if (shouldCloseFreezerBeforeReturn) {
@@ -2077,7 +2083,7 @@
 
                 // Set the update and install times
                 PackageStateInternal deletedPkgSetting = mPm.snapshotComputer()
-                        .getPackageStateInternal(oldPackage.getPackageName());
+                        .getPackageStateInternal(packageName);
                 // TODO(b/225756739): For rebootless APEX, consider using lastUpdateMillis provided
                 //  by apexd to be more accurate.
                 installRequest.setScannedPackageSettingFirstInstallTimeFromReplaced(
@@ -2126,8 +2132,10 @@
                         if (oldCodePaths == null) {
                             oldCodePaths = new ArraySet<>();
                         }
-                        Collections.addAll(oldCodePaths, oldPackage.getBaseApkPath());
-                        Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
+                        if (oldPackage != null) {
+                            Collections.addAll(oldCodePaths, oldPackage.getBaseApkPath());
+                            Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
+                        }
                         ps1.setOldCodePaths(oldCodePaths);
                     } else {
                         ps1.setOldCodePaths(null);
@@ -2161,6 +2169,9 @@
                 }
             }
             if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
+                mPm.createArchiveStateIfNeeded(ps,
+                        installRequest.getArchivedPackage(),
+                        installRequest.getNewUsers());
                 mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
                 mPm.updateInstantAppInstallerLocked(packageName);
             }
@@ -2852,46 +2863,11 @@
             mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(),
                     request.getNewUsers());
 
-            // Determine the set of users who are adding this package for
-            // the first time vs. those who are seeing an update.
-            int[] firstUserIds = EMPTY_INT_ARRAY;
-            int[] firstInstantUserIds = EMPTY_INT_ARRAY;
-            int[] updateUserIds = EMPTY_INT_ARRAY;
-            int[] instantUserIds = EMPTY_INT_ARRAY;
-            final boolean allNewUsers = request.getOriginUsers() == null
-                    || request.getOriginUsers().length == 0;
-            for (int newUser : request.getNewUsers()) {
-                final boolean isInstantApp = pkgSetting.getUserStateOrDefault(newUser)
-                        .isInstantApp();
-                if (allNewUsers) {
-                    if (isInstantApp) {
-                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
-                    } else {
-                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
-                    }
-                    continue;
-                }
-                boolean isNew = true;
-                for (int origUser : request.getOriginUsers()) {
-                    if (origUser == newUser) {
-                        isNew = false;
-                        break;
-                    }
-                }
-                if (isNew) {
-                    if (isInstantApp) {
-                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
-                    } else {
-                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
-                    }
-                } else {
-                    if (isInstantApp) {
-                        instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
-                    } else {
-                        updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
-                    }
-                }
-            }
+            request.populateBroadcastUsers();
+            final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
+            final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
+            final int[] updateUserIds = request.getUpdateBroadcastUserIds();
+            final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
 
             Bundle extras = new Bundle();
             extras.putInt(Intent.EXTRA_UID, request.getAppId());
@@ -2969,12 +2945,10 @@
                 }
                 // If package installer is defined, notify package installer about new
                 // app installed
-                if (mPm.mRequiredInstallerPackage != null) {
-                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
-                            mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
-                            firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
-                }
+                mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+                        extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+                        mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
+                        firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
 
                 // Send replaced for users that don't see the package for the first time
                 if (update) {
@@ -3070,7 +3044,7 @@
                 }
             }
 
-            if (allNewUsers && !update) {
+            if (request.isAllNewUsers() && !update) {
                 mPm.notifyPackageAdded(packageName, request.getAppId());
             } else {
                 mPm.notifyPackageChanged(packageName, request.getAppId());
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index e1cfc41..a4ee3c8 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -21,6 +21,10 @@
 import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.os.Process.INVALID_UID;
+
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
 import static com.android.server.pm.PackageManagerService.TAG;
 
@@ -28,6 +32,7 @@
 import android.annotation.Nullable;
 import android.apex.ApexInfo;
 import android.app.AppOpsManager;
+import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.DataLoaderType;
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.PackageInstaller;
@@ -43,6 +48,7 @@
 import android.util.ExceptionUtils;
 import android.util.Slog;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.server.art.model.DexoptResult;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -52,6 +58,8 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
 import java.util.List;
 
 final class InstallRequest {
@@ -69,11 +77,13 @@
     private int mParseFlags;
     private boolean mReplace;
 
-    @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
-    private AndroidPackage mExistingPackage;
+    @Nullable /* The original package's name if it is being replaced, otherwise {@code null} */
+    private String mExistingPackageName;
     /** parsed package to be scanned */
     @Nullable
     private ParsedPackage mParsedPackage;
+    @Nullable
+    private ArchivedPackageParcel mArchivedPackage;
     private boolean mClearCodeCache;
     private boolean mSystem;
     @Nullable
@@ -132,6 +142,18 @@
 
     private int mDexoptStatus;
 
+    @NonNull
+    private int[] mFirstTimeBroadcastUserIds = EMPTY_INT_ARRAY;
+    @NonNull
+    private int[] mFirstTimeBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+    @NonNull
+    private int[] mUpdateBroadcastUserIds = EMPTY_INT_ARRAY;
+    @NonNull
+    private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+
+    @NonNull
+    private ArrayList<String> mWarnings = new ArrayList<>();
+
     // New install
     InstallRequest(InstallingSession params) {
         mUserId = params.getUser().getIdentifier();
@@ -177,6 +199,7 @@
         }
         mInstallArgs = null;
         mParsedPackage = parsedPackage;
+        mArchivedPackage = null;
         mParseFlags = parseFlags;
         mScanFlags = scanFlags;
         mScanResult = scanResult;
@@ -413,11 +436,6 @@
     }
 
     @Nullable
-    public AndroidPackage getExistingPackage() {
-        return mExistingPackage;
-    }
-
-    @Nullable
     public List<String> getAllowlistedRestrictedPermissions() {
         return mInstallArgs == null ? null : mInstallArgs.mAllowlistedRestrictedPermissions;
     }
@@ -441,6 +459,9 @@
         return mParsedPackage;
     }
 
+    @Nullable
+    public ArchivedPackageParcel getArchivedPackage() { return mArchivedPackage; }
+
     @ParsingPackageUtils.ParseFlags
     public int getParseFlags() {
         return mParseFlags;
@@ -453,10 +474,7 @@
 
     @Nullable
     public String getExistingPackageName() {
-        if (mExistingPackage != null) {
-            return mExistingPackage.getPackageName();
-        }
-        return null;
+        return mExistingPackageName;
     }
 
     @Nullable
@@ -627,6 +645,30 @@
         return mDexoptStatus;
     }
 
+    public boolean isAllNewUsers() {
+        return mOrigUsers == null || mOrigUsers.length == 0;
+    }
+    public int[] getFirstTimeBroadcastUserIds() {
+        return mFirstTimeBroadcastUserIds;
+    }
+
+    public int[] getFirstTimeBroadcastInstantUserIds() {
+        return mFirstTimeBroadcastInstantUserIds;
+    }
+
+    public int[] getUpdateBroadcastUserIds() {
+        return mUpdateBroadcastUserIds;
+    }
+
+    public int[] getUpdateBroadcastInstantUserIds() {
+        return mUpdateBroadcastInstantUserIds;
+    }
+
+    @NonNull
+    public ArrayList<String> getWarnings() {
+        return mWarnings;
+    }
+
     public void setScanFlags(int scanFlags) {
         mScanFlags = scanFlags;
     }
@@ -729,14 +771,17 @@
     }
 
     public void setPrepareResult(boolean replace, int scanFlags,
-            int parseFlags, AndroidPackage existingPackage,
-            ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
+            int parseFlags, PackageState existingPackageState,
+            ParsedPackage packageToScan, ArchivedPackageParcel archivedPackage,
+            boolean clearCodeCache, boolean system,
             PackageSetting originalPs, PackageSetting disabledPs) {
         mReplace = replace;
         mScanFlags = scanFlags;
         mParseFlags = parseFlags;
-        mExistingPackage = existingPackage;
+        mExistingPackageName =
+                existingPackageState != null ? existingPackageState.getPackageName() : null;
         mParsedPackage = packageToScan;
+        mArchivedPackage = archivedPackage;
         mClearCodeCache = clearCodeCache;
         mSystem = system;
         mOriginalPs = originalPs;
@@ -769,6 +814,62 @@
         }
     }
 
+    /**
+     *  Determine the set of users who are adding this package for the first time vs. those who are
+     *  seeing an update.
+     */
+    public void populateBroadcastUsers() {
+        assertScanResultExists();
+        mFirstTimeBroadcastUserIds = EMPTY_INT_ARRAY;
+        mFirstTimeBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+        mUpdateBroadcastUserIds = EMPTY_INT_ARRAY;
+        mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+
+        final boolean allNewUsers = isAllNewUsers();
+        if (allNewUsers) {
+            // App was not currently installed on any user
+            for (int newUser : mNewUsers) {
+                final boolean isInstantApp =
+                        mScanResult.mPkgSetting.getUserStateOrDefault(newUser).isInstantApp();
+                if (isInstantApp) {
+                    mFirstTimeBroadcastInstantUserIds =
+                            ArrayUtils.appendInt(mFirstTimeBroadcastInstantUserIds, newUser);
+                } else {
+                    mFirstTimeBroadcastUserIds =
+                            ArrayUtils.appendInt(mFirstTimeBroadcastUserIds, newUser);
+                }
+            }
+            return;
+        }
+        // App was already installed on some users, but is new to some other users
+        for (int newUser : mNewUsers) {
+            boolean isFirstTimeUser = !ArrayUtils.contains(mOrigUsers, newUser);
+            final boolean isInstantApp =
+                    mScanResult.mPkgSetting.getUserStateOrDefault(newUser).isInstantApp();
+            if (isFirstTimeUser) {
+                if (isInstantApp) {
+                    mFirstTimeBroadcastInstantUserIds =
+                            ArrayUtils.appendInt(mFirstTimeBroadcastInstantUserIds, newUser);
+                } else {
+                    mFirstTimeBroadcastUserIds =
+                            ArrayUtils.appendInt(mFirstTimeBroadcastUserIds, newUser);
+                }
+            } else {
+                if (isInstantApp) {
+                    mUpdateBroadcastInstantUserIds =
+                            ArrayUtils.appendInt(mUpdateBroadcastInstantUserIds, newUser);
+                } else {
+                    mUpdateBroadcastUserIds =
+                            ArrayUtils.appendInt(mUpdateBroadcastUserIds, newUser);
+                }
+            }
+        }
+    }
+
+    public void addWarning(@NonNull String warning) {
+        mWarnings.add(warning);
+    }
+
     public void onPrepareStarted() {
         if (mPackageMetrics != null) {
             mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
@@ -818,22 +919,37 @@
     }
 
     public void onDexoptFinished(DexoptResult dexoptResult) {
-        if (mPackageMetrics == null) {
-            return;
-        }
-        mDexoptStatus = dexoptResult.getFinalStatus();
-        if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) {
-            return;
-        }
-        long durationMillis = 0;
-        for (DexoptResult.PackageDexoptResult packageResult :
-                dexoptResult.getPackageDexoptResults()) {
-            for (DexoptResult.DexContainerFileDexoptResult fileResult :
-                    packageResult.getDexContainerFileDexoptResults()) {
-                durationMillis += fileResult.getDex2oatWallTimeMillis();
+        // Only report external profile warnings when installing from adb. The goal is to warn app
+        // developers if they have provided bad external profiles, so it's not beneficial to report
+        // those warnings in the normal app install workflow.
+        if (isInstallFromAdb()) {
+            var externalProfileErrors = new LinkedHashSet<String>();
+            for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+                for (DexContainerFileDexoptResult fileResult :
+                        packageResult.getDexContainerFileDexoptResults()) {
+                    externalProfileErrors.addAll(fileResult.getExternalProfileErrors());
+                }
+            }
+            if (!externalProfileErrors.isEmpty()) {
+                addWarning("Error occurred during dexopt when processing external profiles:\n  "
+                        + String.join("\n  ", externalProfileErrors));
             }
         }
-        mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+
+        // Report dexopt metrics.
+        if (mPackageMetrics != null) {
+            mDexoptStatus = dexoptResult.getFinalStatus();
+            if (mDexoptStatus == DexoptResult.DEXOPT_PERFORMED) {
+                long durationMillis = 0;
+                for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+                    for (DexContainerFileDexoptResult fileResult :
+                            packageResult.getDexContainerFileDexoptResults()) {
+                        durationMillis += fileResult.getDex2oatWallTimeMillis();
+                    }
+                }
+                mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+            }
+        }
     }
 
     public void onInstallCompleted() {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 6233c9b..0ebd33b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -257,6 +257,7 @@
     private static CreateAppDataResult buildPlaceholderCreateAppDataResult() {
         final CreateAppDataResult result = new CreateAppDataResult();
         result.ceDataInode = -1;
+        result.deDataInode = -1;
         result.exceptionCode = 0;
         result.exceptionMessage = null;
         return result;
@@ -361,7 +362,7 @@
         private boolean mExecuted;
 
         private final List<CreateAppDataArgs> mArgs = new ArrayList<>();
-        private final List<CompletableFuture<Long>> mFutures = new ArrayList<>();
+        private final List<CompletableFuture<CreateAppDataResult>> mFutures = new ArrayList<>();
 
         /**
          * Enqueue the given {@code installd} operation to be executed in the
@@ -371,11 +372,12 @@
          * {@link Installer} object.
          */
         @NonNull
-        public synchronized CompletableFuture<Long> createAppData(CreateAppDataArgs args) {
+        public synchronized CompletableFuture<CreateAppDataResult> createAppData(
+                CreateAppDataArgs args) {
             if (mExecuted) {
                 throw new IllegalStateException();
             }
-            final CompletableFuture<Long> future = new CompletableFuture<>();
+            final CompletableFuture<CreateAppDataResult> future = new CompletableFuture<>();
             mArgs.add(args);
             mFutures.add(future);
             return future;
@@ -402,9 +404,9 @@
                 final CreateAppDataResult[] results = installer.createAppDataBatched(args);
                 for (int j = 0; j < results.length; j++) {
                     final CreateAppDataResult result = results[j];
-                    final CompletableFuture<Long> future = mFutures.get(i + j);
+                    final CompletableFuture<CreateAppDataResult> future = mFutures.get(i + j);
                     if (result.exceptionCode == 0) {
-                        future.complete(result.ceDataInode);
+                        future.complete(result);
                     } else {
                         future.completeExceptionally(
                                 new InstallerException(result.exceptionMessage));
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 64cdca3..803b6e4 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -31,12 +32,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ArchivedActivityParcel;
+import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -56,6 +60,7 @@
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageUserStateInternal;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -138,7 +143,10 @@
                             mPm.mInstallerService.uninstall(
                                     new VersionedPackage(packageName,
                                             PackageManager.VERSION_CODE_HIGHEST),
-                                    callerPackageName, DELETE_KEEP_DATA, intentSender, userId,
+                                    callerPackageName,
+                                    DELETE_ARCHIVE | DELETE_KEEP_DATA,
+                                    intentSender,
+                                    userId,
                                     binderUid);
                         })
                 .exceptionally(
@@ -158,7 +166,8 @@
         String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
         verifyInstaller(responsibleInstallerPackage);
 
-        List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps, userId);
+        List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(),
+                userId);
         final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>();
         mPm.mHandler.post(() -> {
             try {
@@ -172,13 +181,34 @@
         return archiveState;
     }
 
-    private ArchiveState createArchiveStateInternal(String packageName, int userId,
+    static ArchiveState createArchiveState(@NonNull ArchivedPackageParcel archivedPackage,
+            int userId, String installerPackage) {
+        try {
+            var packageName = archivedPackage.packageName;
+            var mainActivities = archivedPackage.archivedActivities;
+            List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.length);
+            for (int i = 0, size = mainActivities.length; i < size; ++i) {
+                var mainActivity = mainActivities[i];
+                Path iconPath = storeIconForParcel(packageName, mainActivity, userId, i);
+                ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
+                        mainActivity.title, iconPath, null);
+                archiveActivityInfos.add(activityInfo);
+            }
+
+            return new ArchiveState(archiveActivityInfos, installerPackage);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to create archive state", e);
+            return null;
+        }
+    }
+
+    ArchiveState createArchiveStateInternal(String packageName, int userId,
             List<LauncherActivityInfo> mainActivities, String installerPackage)
             throws IOException {
-        List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>();
-        for (int i = 0; i < mainActivities.size(); i++) {
+        List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.size());
+        for (int i = 0, size = mainActivities.size(); i < size; i++) {
             LauncherActivityInfo mainActivity = mainActivities.get(i);
-            Path iconPath = storeIcon(packageName, mainActivity, userId);
+            Path iconPath = storeIcon(packageName, mainActivity, userId, i);
             ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
                     mainActivity.getLabel().toString(), iconPath, null);
             archiveActivityInfos.add(activityInfo);
@@ -188,17 +218,30 @@
     }
 
     // TODO(b/298452477) Handle monochrome icons.
+    private static Path storeIconForParcel(String packageName, ArchivedActivityParcel mainActivity,
+            @UserIdInt int userId, int index) throws IOException {
+        if (mainActivity.iconBitmap == null) {
+            return null;
+        }
+        File iconsDir = createIconsDir(userId);
+        File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
+        try (FileOutputStream out = new FileOutputStream(iconFile)) {
+            out.write(mainActivity.iconBitmap);
+            out.flush();
+        }
+        return iconFile.toPath();
+    }
+
     @VisibleForTesting
     Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
-            @UserIdInt int userId)
-            throws IOException {
+            @UserIdInt int userId, int index) throws IOException {
         int iconResourceId = mainActivity.getActivityInfo().getIconResource();
         if (iconResourceId == 0) {
             // The app doesn't define an icon. No need to store anything.
             return null;
         }
         File iconsDir = createIconsDir(userId);
-        File iconFile = new File(iconsDir, packageName + "-" + mainActivity.getName() + ".png");
+        File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
         Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0));
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
             // Note: Quality is ignored for PNGs.
@@ -228,6 +271,9 @@
      */
     public boolean verifySupportsUnarchival(String installerPackage) {
         // TODO(b/278553670) Check if installerPackage supports unarchival.
+        if (TextUtils.isEmpty(installerPackage)) {
+            return false;
+        }
         return true;
     }
 
@@ -265,6 +311,46 @@
         mPm.mHandler.post(() -> unarchiveInternal(packageName, userHandle, installerPackage));
     }
 
+    /**
+     * Returns the icon of an archived app. This is the icon of the main activity of the app.
+     *
+     * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
+     * launcher activities, only one of the icons is returned arbitrarily.
+     */
+    public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(user);
+
+        Computer snapshot = mPm.snapshotComputer();
+        int callingUid = Binder.getCallingUid();
+        int userId = user.getIdentifier();
+        PackageStateInternal ps;
+        try {
+            ps = getPackageState(packageName, snapshot, callingUid, userId);
+            snapshot.enforceCrossUserPermission(callingUid, userId, true, false,
+                    "getArchivedAppIcon");
+            verifyArchived(ps, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new ParcelableException(e);
+        }
+
+        List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault(
+                userId).getArchiveState().getActivityInfos();
+        if (activityInfos.size() == 0) {
+            return null;
+        }
+
+        // TODO(b/298452477) Handle monochrome icons.
+        // In the rare case the archived app defined more than two launcher activities, we choose
+        // the first one arbitrarily.
+        return decodeIcon(activityInfos.get(0));
+    }
+
+    @VisibleForTesting
+    Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) {
+        return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
+    }
+
     private void verifyArchived(PackageStateInternal ps, int userId)
             throws PackageManager.NameNotFoundException {
         PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
@@ -310,16 +396,16 @@
                 /* initialExtras= */ null);
     }
 
-    private List<LauncherActivityInfo> getLauncherActivityInfos(PackageStateInternal ps,
+    List<LauncherActivityInfo> getLauncherActivityInfos(String packageName,
             int userId) throws PackageManager.NameNotFoundException {
         List<LauncherActivityInfo> mainActivities =
                 Binder.withCleanCallingIdentity(() -> getLauncherApps().getActivityList(
-                        ps.getPackageName(),
+                        packageName,
                         new UserHandle(userId)));
         if (mainActivities.isEmpty()) {
             throw new PackageManager.NameNotFoundException(
                     TextUtils.formatSimple("The app %s does not have a main activity.",
-                            ps.getPackageName()));
+                            packageName));
         }
 
         return mainActivities;
@@ -340,7 +426,7 @@
         return DEFAULT_UNARCHIVE_FOREGROUND_TIMEOUT_MS;
     }
 
-    private String getResponsibleInstallerPackage(PackageStateInternal ps) {
+    static String getResponsibleInstallerPackage(PackageStateInternal ps) {
         return TextUtils.isEmpty(ps.getInstallSource().mUpdateOwnerPackageName)
                 ? ps.getInstallSource().mInstallerPackageName
                 : ps.getInstallSource().mUpdateOwnerPackageName;
@@ -423,7 +509,7 @@
         }
     }
 
-    private File createIconsDir(@UserIdInt int userId) throws IOException {
+    private static File createIconsDir(@UserIdInt int userId) throws IOException {
         File iconsDir = getIconsDir(userId);
         if (!iconsDir.isDirectory()) {
             iconsDir.delete();
@@ -436,7 +522,7 @@
         return iconsDir;
     }
 
-    private File getIconsDir(int userId) {
+    private static File getIconsDir(int userId) {
         return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR);
     }
 
@@ -462,4 +548,90 @@
         drawable.draw(canvas);
         return bitmap;
     }
+
+    private static byte[] bytesFromBitmapFile(Path path) throws IOException {
+        if (path == null) {
+            return null;
+        }
+        // Technically we could just read the bytes, but we want to be sure we store the
+        // right format.
+        return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
+    }
+
+    private static byte[] bytesFromBitmap(Bitmap bitmap) throws IOException {
+        if (bitmap == null) {
+            return null;
+        }
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(
+                bitmap.getByteCount())) {
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+            return baos.toByteArray();
+        }
+    }
+
+    /**
+     * Creates serializable archived activities from existing ArchiveState.
+     */
+    static ArchivedActivityParcel[] createArchivedActivities(ArchiveState archiveState)
+            throws IOException {
+        var infos = archiveState.getActivityInfos();
+        if (infos == null || infos.isEmpty()) {
+            throw new IllegalArgumentException("No activities in archive state");
+        }
+
+        List<ArchivedActivityParcel> activities = new ArrayList<>(infos.size());
+        for (int i = 0, size = infos.size(); i < size; ++i) {
+            var info = infos.get(i);
+            if (info == null) {
+                continue;
+            }
+            var archivedActivity = new ArchivedActivityParcel();
+            archivedActivity.title = info.getTitle();
+            archivedActivity.iconBitmap = bytesFromBitmapFile(info.getIconBitmap());
+            archivedActivity.monochromeIconBitmap = bytesFromBitmapFile(
+                    info.getMonochromeIconBitmap());
+            activities.add(archivedActivity);
+        }
+
+        if (activities.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Failed to extract title and icon of main activities");
+        }
+
+        return activities.toArray(new ArchivedActivityParcel[activities.size()]);
+    }
+
+    /**
+     * Creates serializable archived activities from launcher activities.
+     */
+    static ArchivedActivityParcel[] createArchivedActivities(List<LauncherActivityInfo> infos)
+            throws IOException {
+        if (infos == null || infos.isEmpty()) {
+            throw new IllegalArgumentException("No launcher activities");
+        }
+
+        List<ArchivedActivityParcel> activities = new ArrayList<>(infos.size());
+        for (int i = 0, size = infos.size(); i < size; ++i) {
+            var info = infos.get(i);
+            if (info == null) {
+                continue;
+            }
+            var archivedActivity = new ArchivedActivityParcel();
+            archivedActivity.title = info.getLabel().toString();
+            archivedActivity.iconBitmap =
+                    info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap(
+                            drawableToBitmap(info.getIcon(/* density= */ 0)));
+            // TODO(b/298452477) Handle monochrome icons.
+            archivedActivity.monochromeIconBitmap = null;
+            activities.add(archivedActivity);
+        }
+
+        if (activities.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Failed to extract title and icon of main activities");
+        }
+
+        return activities.toArray(new ArchivedActivityParcel[activities.size()]);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3364b67..f554773 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -40,6 +40,7 @@
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
 import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
@@ -1167,11 +1168,6 @@
                 throw new IllegalArgumentException(
                         "Archived installation can only use Streaming System DataLoader.");
             }
-            if (!TextUtils.isEmpty(params.appPackageName) && !isArchivedInstallationAllowed(
-                    params.appPackageName)) {
-                throw new IllegalArgumentException(
-                        "Archived installation of this package is not allowed.");
-            }
         }
     }
 
@@ -1565,6 +1561,10 @@
             }
 
             var archPkg = metadata.getArchivedPackage();
+            if (archPkg == null) {
+                throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
+                        "Metadata does not contain ArchivedPackage: " + file);
+            }
             if (archPkg.packageName == null || archPkg.signingDetails == null) {
                 throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
                         "ArchivedPackage does not contain required info: " + file);
@@ -3420,8 +3420,7 @@
                         "Archived installation of this package is not allowed.");
             }
 
-            if (!isInstalledByAdb(getInstallSource().mInitiatingPackageName)
-                    && !mPm.mInstallerService.mPackageArchiver.verifySupportsUnarchival(
+            if (!mPm.mInstallerService.mPackageArchiver.verifySupportsUnarchival(
                     getInstallSource().mInstallerPackageName)) {
                 throw new PackageManagerException(
                         PackageManager.INSTALL_FAILED_SESSION_INVALID,
@@ -5190,6 +5189,10 @@
             if (!TextUtils.isEmpty(existing)) {
                 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
             }
+            ArrayList<String> warnings = extras.getStringArrayList(PackageInstaller.EXTRA_WARNINGS);
+            if (!ArrayUtils.isEmpty(warnings)) {
+                fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
+            }
         }
         try {
             final BroadcastOptions options = BroadcastOptions.makeBasic();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d23dcbc..700fae9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -36,6 +36,7 @@
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
 import static com.android.server.pm.DexOptHelper.useArtService;
@@ -116,11 +117,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.parsing.ApkLite;
-import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -228,6 +225,7 @@
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.ArchiveState;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageUserState;
@@ -1435,9 +1433,98 @@
                 break;
             }
         }
+        if (!request.getWarnings().isEmpty()) {
+            extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings());
+        }
         return extras;
     }
 
+    ArchivedPackageParcel getArchivedPackageInternal(@NonNull String packageName, int userId) {
+        Objects.requireNonNull(packageName);
+        int binderUid = Binder.getCallingUid();
+
+        Computer snapshot = snapshotComputer();
+        snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
+                "getArchivedPackage");
+
+        ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
+        archPkg.packageName = packageName;
+
+        ArchiveState archiveState;
+        synchronized (mLock) {
+            PackageSetting ps = mSettings.getPackageLPr(packageName);
+            if (ps == null) {
+                return null;
+            }
+            var psi = ps.getUserStateOrDefault(userId);
+            archiveState = psi.getArchiveState();
+            if (archiveState == null && !psi.isInstalled()) {
+                return null;
+            }
+
+            archPkg.signingDetails = ps.getSigningDetails();
+
+            long longVersionCode = ps.getVersionCode();
+            archPkg.versionCodeMajor = (int) (longVersionCode >> 32);
+            archPkg.versionCode = (int) longVersionCode;
+
+            // TODO(b/297916136): extract target sdk version.
+            archPkg.targetSdkVersion = MIN_INSTALLABLE_TARGET_SDK;
+
+            // These get translated in flags important for user data management.
+            archPkg.defaultToDeviceProtectedStorage = String.valueOf(
+                    ps.isDefaultToDeviceProtectedStorage());
+            archPkg.requestLegacyExternalStorage = String.valueOf(
+                    ps.isRequestLegacyExternalStorage());
+            archPkg.userDataFragile = String.valueOf(ps.isUserDataFragile());
+        }
+
+        try {
+            if (archiveState != null) {
+                archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
+                        archiveState);
+            } else {
+                var mainActivities =
+                        mInstallerService.mPackageArchiver.getLauncherActivityInfos(packageName,
+                                userId);
+                archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
+                        mainActivities);
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Package does not have a main activity", e);
+        }
+
+        return archPkg;
+    }
+
+    void createArchiveStateIfNeeded(PackageSetting pkgSetting, ArchivedPackageParcel archivePackage,
+            int[] userIds) {
+        if (pkgSetting == null || archivePackage == null
+                || archivePackage.archivedActivities == null || userIds == null
+                || userIds.length == 0) {
+            return;
+        }
+
+        String responsibleInstallerPackage = PackageArchiver.getResponsibleInstallerPackage(
+                pkgSetting);
+        // TODO(b/278553670) Check if responsibleInstallerPackage supports unarchival.
+        if (TextUtils.isEmpty(responsibleInstallerPackage)) {
+            Slog.e(TAG, "Can't create archive state: responsible installer is empty");
+            return;
+        }
+        for (int userId : userIds) {
+            var archiveState = PackageArchiver.createArchiveState(archivePackage, userId,
+                    responsibleInstallerPackage);
+            if (archiveState == null) {
+                continue;
+            }
+            pkgSetting
+                    .modifyUserState(userId)
+                    .setArchiveState(archiveState);
+        }
+    }
+
+
     void scheduleWriteSettings() {
         // We normally invalidate when we write settings, but in cases where we delay and
         // coalesce settings writes, this strategy would have us invalidate the cache too late.
@@ -6301,35 +6388,13 @@
         }
 
         @Override
-        public ArchivedPackageParcel getArchivedPackage(String apkPath) {
-            ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
-            ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
-                    new File(apkPath), ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
-            if (result.isError()) {
-                throw new IllegalArgumentException(result.getErrorMessage(), result.getException());
-            }
-            final ApkLite apk = result.getResult();
+        public ArchivedPackageParcel getArchivedPackage(@NonNull String packageName, int userId) {
+            return getArchivedPackageInternal(packageName, userId);
+        }
 
-            ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
-            archPkg.packageName = apk.getPackageName();
-            archPkg.signingDetails = apk.getSigningDetails();
-
-            archPkg.versionCodeMajor = apk.getVersionCodeMajor();
-            archPkg.versionCode = apk.getVersionCode();
-
-            archPkg.targetSdkVersion = apk.getTargetSdkVersion();
-
-            // These get translated in flags important for user data management.
-            archPkg.backupAllowed = String.valueOf(apk.isBackupAllowed());
-            archPkg.defaultToDeviceProtectedStorage = String.valueOf(
-                    apk.isDefaultToDeviceProtectedStorage());
-            archPkg.requestLegacyExternalStorage = String.valueOf(
-                    apk.isRequestLegacyExternalStorage());
-            archPkg.userDataFragile = String.valueOf(apk.isUserDataFragile());
-            archPkg.clearUserDataOnFailedRestoreAllowed = String.valueOf(
-                    apk.isClearUserDataOnFailedRestoreAllowed());
-
-            return archPkg;
+        @Override
+        public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) {
+            return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user);
         }
 
         /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8d82085..72a7370 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -26,6 +26,7 @@
 import static android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
 import static android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS;
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
+
 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
@@ -82,9 +83,9 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.IBinder;
 import android.os.IUserManager;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.os.PersistableBundle;
@@ -130,6 +131,7 @@
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
+import libcore.util.HexEncoding;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -248,8 +250,6 @@
                     return runStreamingInstall();
                 case "install-incremental":
                     return runIncrementalInstall();
-                case "install-archived":
-                    return runArchivedInstall();
                 case "install-abandon":
                 case "install-destroy":
                     return runInstallAbandon();
@@ -277,6 +277,10 @@
                     return runUninstall();
                 case "clear":
                     return runClear();
+                case "get-archived-package-metadata":
+                    return runGetArchivedPackageMetadata();
+                case "install-archived":
+                    return runArchivedInstall();
                 case "enable":
                     return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
                 case "disable":
@@ -1808,6 +1812,56 @@
         return doRemoveSplits(sessionId, splitNames, true /*logSuccess*/);
     }
 
+    private int runGetArchivedPackageMetadata() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        int userId = UserHandle.USER_CURRENT;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "--user":
+                    userId = UserHandle.parseUserArg(getNextArgRequired());
+                    break;
+                default:
+                    pw.println("Error: Unknown option: " + opt);
+                    return 1;
+            }
+        }
+
+        final String packageName = getNextArg();
+        if (packageName == null) {
+            pw.println("Error: package name not specified");
+            return 1;
+        }
+        final int translatedUserId = translateUserId(userId, UserHandle.USER_NULL,
+                "runGetArchivedPackageMetadata");
+
+        try {
+            var archivedPackage = mInterface.getArchivedPackage(packageName, translatedUserId);
+            if (archivedPackage == null) {
+                pw.write("Package not found " + packageName);
+                return -1;
+            }
+
+            Parcel parcel = Parcel.obtain();
+            byte[] bytes;
+            try {
+                parcel.writeParcelable(archivedPackage, 0);
+                bytes = parcel.marshall();
+            } finally {
+                parcel.recycle();
+            }
+
+            String encoded = HexEncoding.encodeToString(bytes);
+            pw.write(encoded);
+        } catch (Exception e) {
+            getErrPrintWriter().println("Failed to get archived package, reason: " + e);
+            pw.println("Failure [failed to get archived package], reason: " + e);
+            return -1;
+        }
+        return 0;
+    }
+
     private int runInstallExisting() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         int userId = UserHandle.USER_CURRENT;
@@ -4146,28 +4200,24 @@
             throw new IllegalArgumentException("Error: Can't open file: " + inPath);
         }
 
-        File tmpFile = null;
+        final String encoded;
         final ParcelFileDescriptor fd = fdWithSize.first;
+        final int size = (int) (long) fdWithSize.second;
         try (InputStream inStream = new AutoCloseInputStream(fd)) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                File tmpStagingDir = Environment.getDataAppDirectory(null);
-                tmpFile = new File(tmpStagingDir, "tmdl" + RANDOM.nextInt() + ".tmp");
-
-                try (OutputStream outStream = new FileOutputStream(tmpFile)) {
-                    Streams.copy(inStream, outStream);
-                }
-
-                return mInterface.getArchivedPackage(tmpFile.getAbsolutePath());
-            } finally {
-                if (tmpFile != null) {
-                    tmpFile.delete();
-                }
-                Binder.restoreCallingIdentity(identity);
-            }
+            byte[] bytes = new byte[size];
+            Streams.readFully(inStream, bytes);
+            encoded = new String(bytes);
         } catch (IOException e) {
-            throw new IllegalArgumentException("Error: Can't stage file: " + inPath, e);
+            throw new IllegalArgumentException("Error: Can't load archived package from: " + inPath,
+                    e);
         }
+
+        var result = Metadata.readArchivedPackageParcel(HexEncoding.decode(encoded));
+        if (result == null) {
+            throw new IllegalArgumentException(
+                    "Error: Can't parse archived package from: " + inPath);
+        }
+        return result;
     }
 
     private void processArgForLocalFile(String arg, PackageInstaller.Session session,
@@ -4347,10 +4397,21 @@
             session.commit(receiver.getIntentSender());
             if (!session.isStaged()) {
                 final Intent result = receiver.getResult();
-                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                        PackageInstaller.STATUS_FAILURE);
+                int status = result.getIntExtra(
+                        PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+                List<String> warnings =
+                        result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS);
                 if (status == PackageInstaller.STATUS_SUCCESS) {
-                    if (logSuccess) {
+                    if (!ArrayUtils.isEmpty(warnings)) {
+                        // Don't start the output string with "Success" because that will make adb
+                        // treat this as a success.
+                        for (String warning : warnings) {
+                            pw.println("Warning: " + warning);
+                        }
+                        // Treat warnings as failure to draw app developers' attention.
+                        status = PackageInstaller.STATUS_FAILURE;
+                        pw.println("Completed with warning(s)");
+                    } else if (logSuccess) {
                         pw.println("Success");
                     }
                 } else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index fbe5a51..9e7f043 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -166,16 +166,7 @@
         /** @hide */
         @VisibleForTesting
         public static Metadata forArchived(ArchivedPackageParcel archivedPackage) {
-            Parcel parcel = Parcel.obtain();
-            byte[] bytes;
-            try {
-                parcel.writeParcelable(archivedPackage, 0);
-                bytes = parcel.marshall();
-            } finally {
-                parcel.recycle();
-            }
-
-            return new Metadata(ARCHIVED, bytes, null);
+            return new Metadata(ARCHIVED, writeArchivedPackageParcel(archivedPackage), null);
         }
 
         static Metadata forDataOnlyStreaming(String fileId) {
@@ -270,11 +261,14 @@
             if (getMode() != ARCHIVED) {
                 throw new IllegalStateException("Not an archived package metadata.");
             }
+            return readArchivedPackageParcel(this.mData);
+        }
 
+        static ArchivedPackageParcel readArchivedPackageParcel(byte[] bytes) {
             Parcel parcel = Parcel.obtain();
             ArchivedPackageParcel result;
             try {
-                parcel.unmarshall(this.mData, 0, this.mData.length);
+                parcel.unmarshall(bytes, 0, bytes.length);
                 parcel.setDataPosition(0);
                 result = parcel.readParcelable(ArchivedPackageParcel.class.getClassLoader());
             } finally {
@@ -282,6 +276,16 @@
             }
             return result;
         }
+
+        static byte[] writeArchivedPackageParcel(ArchivedPackageParcel archivedPackage) {
+            Parcel parcel = Parcel.obtain();
+            try {
+                parcel.writeParcelable(archivedPackage, 0);
+                return parcel.marshall();
+            } finally {
+                parcel.recycle();
+            }
+        }
     }
 
     private static class DataLoader implements DataLoaderService.DataLoader {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 114f80d..2e60064 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -654,6 +654,16 @@
         return (getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
+    public boolean isRequestLegacyExternalStorage() {
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
+                != 0;
+    }
+
+    public boolean isUserDataFragile() {
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
+                != 0;
+    }
+
     public SigningDetails getSigningDetails() {
         return signatures.mSigningDetails;
     }
@@ -844,15 +854,42 @@
         return res;
     }
 
+    int[] queryUsersInstalledOrHasData(int[] users) {
+        int num = 0;
+        for (int user : users) {
+            if (getInstalled(user) || readUserState(user).dataExists()) {
+                num++;
+            }
+        }
+        int[] res = new int[num];
+        num = 0;
+        for (int user : users) {
+            if (getInstalled(user) || readUserState(user).dataExists()) {
+                res[num] = user;
+                num++;
+            }
+        }
+        return res;
+    }
+
     long getCeDataInode(int userId) {
         return readUserState(userId).getCeDataInode();
     }
 
+    long getDeDataInode(int userId) {
+        return readUserState(userId).getDeDataInode();
+    }
+
     void setCeDataInode(long ceDataInode, int userId) {
         modifyUserState(userId).setCeDataInode(ceDataInode);
         onChanged();
     }
 
+    void setDeDataInode(long deDataInode, int userId) {
+        modifyUserState(userId).setDeDataInode(deDataInode);
+        onChanged();
+    }
+
     boolean getStopped(int userId) {
         return readUserState(userId).isStopped();
     }
@@ -907,17 +944,18 @@
         onChanged();
     }
 
-    void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
-            boolean notLaunched, boolean hidden, int distractionFlags,
-            ArrayMap<String, SuspendParams> suspendParams, boolean instantApp,
-            boolean virtualPreload, String lastDisableAppCaller,
-            ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
-            int installReason, int uninstallReason,
-            String harmfulAppWarning, String splashScreenTheme,
-            long firstInstallTime, int aspectRatio, ArchiveState archiveState) {
+    void setUserState(int userId, long ceDataInode, long deDataInode, int enabled,
+                      boolean installed, boolean stopped, boolean notLaunched, boolean hidden,
+                      int distractionFlags, ArrayMap<String, SuspendParams> suspendParams,
+                      boolean instantApp, boolean virtualPreload, String lastDisableAppCaller,
+                      ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
+                      int installReason, int uninstallReason,
+                      String harmfulAppWarning, String splashScreenTheme,
+                      long firstInstallTime, int aspectRatio, ArchiveState archiveState) {
         modifyUserState(userId)
                 .setSuspendParams(suspendParams)
                 .setCeDataInode(ceDataInode)
+                .setDeDataInode(deDataInode)
                 .setEnabledState(enabled)
                 .setInstalled(installed)
                 .setStopped(stopped)
@@ -940,9 +978,9 @@
     }
 
     void setUserState(int userId, PackageUserStateInternal otherState) {
-        setUserState(userId, otherState.getCeDataInode(), otherState.getEnabledState(),
-                otherState.isInstalled(), otherState.isStopped(), otherState.isNotLaunched(),
-                otherState.isHidden(), otherState.getDistractionFlags(),
+        setUserState(userId, otherState.getCeDataInode(), otherState.getDeDataInode(),
+                otherState.getEnabledState(), otherState.isInstalled(), otherState.isStopped(),
+                otherState.isNotLaunched(), otherState.isHidden(), otherState.getDistractionFlags(),
                 otherState.getSuspendParams() == null
                         ? null : otherState.getSuspendParams().untrackedStorage(),
                 otherState.isInstantApp(), otherState.isVirtualPreload(),
@@ -1677,10 +1715,10 @@
     }
 
     @DataClass.Generated(
-            time = 1691185420362L,
+            time = 1694196905013L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic  com.android.server.pm.PackageSetting setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int DEFAULT_TO_DEVICE_PROTECTED_STORAGE\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic  com.android.server.pm.PackageSetting setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int DEFAULT_TO_DEVICE_PROTECTED_STORAGE\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 8dec425..d989c90 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -277,6 +277,7 @@
                 mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
                         FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
                 ps.setCeDataInode(-1, nextUserId);
+                ps.setDeDataInode(-1, nextUserId);
             }
             mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
             preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 111a32d..c263978 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -327,6 +327,7 @@
     private static final String ATTR_VERSION = "version";
 
     private static final String ATTR_CE_DATA_INODE = "ceDataInode";
+    private static final String ATTR_DE_DATA_INODE = "deDataInode";
     private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_STOPPED = "stopped";
     private static final String ATTR_NOT_LAUNCHED = "nl";
@@ -1121,7 +1122,7 @@
                                     + "installed=%b)",
                                     pkgName, installUserId, user.toFullString(), installed);
                         }
-                        pkgSetting.setUserState(user.id, 0, COMPONENT_ENABLED_STATE_DEFAULT,
+                        pkgSetting.setUserState(user.id, 0, 0, COMPONENT_ENABLED_STATE_DEFAULT,
                                 installed,
                                 true /*stopped*/,
                                 true /*notLaunched*/,
@@ -1798,7 +1799,8 @@
                         // in the stopped state, but not at first boot.  Also
                         // consider all applications to be installed.
                         for (PackageSetting pkg : mPackages.values()) {
-                            pkg.setUserState(userId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
+                            pkg.setUserState(userId, pkg.getCeDataInode(userId),
+                                    pkg.getDeDataInode(userId), COMPONENT_ENABLED_STATE_DEFAULT,
                                     true  /*installed*/,
                                     false /*stopped*/,
                                     false /*notLaunched*/,
@@ -1861,6 +1863,8 @@
 
                         final long ceDataInode =
                                 parser.getAttributeLong(null, ATTR_CE_DATA_INODE, 0);
+                        final long deDataInode =
+                                parser.getAttributeLong(null, ATTR_DE_DATA_INODE, 0);
                         final boolean installed =
                                 parser.getAttributeBoolean(null, ATTR_INSTALLED, true);
                         final boolean stopped =
@@ -1989,7 +1993,8 @@
                         if (blockUninstall) {
                             setBlockUninstallLPw(userId, name, true);
                         }
-                        ps.setUserState(userId, ceDataInode, enabled, installed, stopped,
+                        ps.setUserState(
+                                userId, ceDataInode, deDataInode, enabled, installed, stopped,
                                 notLaunched, hidden, distractionFlags, suspendParamsMap, instantApp,
                                 virtualPreload, enabledCaller, enabledComponents,
                                 disabledComponents, installReason, uninstallReason,
@@ -2306,6 +2311,10 @@
                             serializer.attributeLong(null, ATTR_CE_DATA_INODE,
                                     ustate.getCeDataInode());
                         }
+                        if (ustate.getDeDataInode() != 0) {
+                            serializer.attributeLong(null, ATTR_DE_DATA_INODE,
+                                    ustate.getDeDataInode());
+                        }
                         if (!ustate.isInstalled()) {
                             serializer.attributeBoolean(null, ATTR_INSTALLED, false);
                         }
@@ -5172,6 +5181,8 @@
             pw.print(prefix); pw.print("  User "); pw.print(user.id); pw.print(": ");
             pw.print("ceDataInode=");
             pw.print(userState.getCeDataInode());
+            pw.print(" deDataInode=");
+            pw.print(userState.getDeDataInode());
             pw.print(" installed=");
             pw.print(userState.isInstalled());
             pw.print(" hidden=");
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 4eceb77..cc8e624 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -378,7 +378,6 @@
         ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
                 | flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
                 | flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
-
         if ((flags & PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS) != 0
                 && state.isQuarantined()) {
             ai.enabled = false;
@@ -402,6 +401,14 @@
             ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
             ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
         }
+        ai.isArchived = isArchived(state);
+    }
+
+    // TODO(b/288142708) Check for userState.isInstalled() here once this bug is fixed.
+    // If an app has isInstalled() == true - it should not be filtered above in any case, currently
+    // it is.
+    private static boolean isArchived(PackageUserState userState) {
+        return userState.getArchiveState() != null;
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index c44b8852..d6a7dc6 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -218,6 +218,7 @@
         STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
         STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
         STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED);
     }
 
     private static final Set<String> NEARBY_DEVICES_PERMISSIONS = new ArraySet<>();
@@ -1490,12 +1491,9 @@
         if (dir.isDirectory() && dir.canRead()) {
             Collections.addAll(ret, dir.listFiles());
         }
-        // For IoT devices, we check the oem partition for default permissions for each app.
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
-            dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
-            if (dir.isDirectory() && dir.canRead()) {
-                Collections.addAll(ret, dir.listFiles());
-            }
+        dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
+        if (dir.isDirectory() && dir.canRead()) {
+            Collections.addAll(ret, dir.listFiles());
         }
         return ret.isEmpty() ? null : ret.toArray(new File[0]);
     }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index c05b3c2..2a81a86 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -86,6 +86,13 @@
     long getCeDataInode();
 
     /**
+     * Device encrypted /data partition inode.
+     *
+     * @hide
+     */
+    long getDeDataInode();
+
+    /**
      * Fully qualified class names of components explicitly disabled.
      *
      * @hide
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index fc4b686..2f4ad2d8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -77,6 +77,11 @@
     }
 
     @Override
+    public long getDeDataInode() {
+        return 0;
+    }
+
+    @Override
     public int getDistractionFlags() {
         return 0;
     }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 0b35d8a..a76a7ce0 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -91,6 +91,7 @@
     protected WatchedArraySet<String> mEnabledComponentsWatched;
 
     private long mCeDataInode;
+    private long mDeDataInode;
     private int mDistractionFlags;
     @PackageManager.EnabledState
     private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -172,6 +173,7 @@
         mSharedLibraryOverlayPaths = other.mSharedLibraryOverlayPaths == null
                 ? null : other.mSharedLibraryOverlayPaths.snapshot();
         mCeDataInode = other.mCeDataInode;
+        mDeDataInode = other.mDeDataInode;
         mDistractionFlags = other.mDistractionFlags;
         mEnabledState = other.mEnabledState;
         mInstallReason = other.mInstallReason;
@@ -391,29 +393,27 @@
     }
 
     public @NonNull PackageUserStateImpl setDisabledComponents(@Nullable ArraySet<String> value) {
-        if (value == null) {
-            return this;
-        }
         if (mDisabledComponentsWatched == null) {
             mDisabledComponentsWatched = new WatchedArraySet<>();
             mDisabledComponentsWatched.registerObserver(mSnapshot);
         }
         mDisabledComponentsWatched.clear();
-        mDisabledComponentsWatched.addAll(value);
+        if (value != null) {
+            mDisabledComponentsWatched.addAll(value);
+        }
         onChanged();
         return this;
     }
 
     public @NonNull PackageUserStateImpl setEnabledComponents(@Nullable ArraySet<String> value) {
-        if (value == null) {
-            return this;
-        }
         if (mEnabledComponentsWatched == null) {
             mEnabledComponentsWatched = new WatchedArraySet<>();
             mEnabledComponentsWatched.registerObserver(mSnapshot);
         }
         mEnabledComponentsWatched.clear();
-        mEnabledComponentsWatched.addAll(value);
+        if (value != null) {
+            mEnabledComponentsWatched.addAll(value);
+        }
         onChanged();
         return this;
     }
@@ -444,6 +444,12 @@
         return this;
     }
 
+    public @NonNull PackageUserStateImpl setDeDataInode(long value) {
+        mDeDataInode = value;
+        onChanged();
+        return this;
+    }
+
     public @NonNull PackageUserStateImpl setInstalled(boolean value) {
         setBoolean(Booleans.INSTALLED, value);
         onChanged();
@@ -687,7 +693,7 @@
 
     @Override
     public boolean dataExists() {
-        return getCeDataInode() > 0;
+        return getCeDataInode() > 0 || getDeDataInode() > 0;
     }
 
 
@@ -721,6 +727,11 @@
     }
 
     @DataClass.Generated.Member
+    public long getDeDataInode() {
+        return mDeDataInode;
+    }
+
+    @DataClass.Generated.Member
     public int getDistractionFlags() {
         return mDistractionFlags;
     }
@@ -849,6 +860,7 @@
                 && Objects.equals(mDisabledComponentsWatched, that.mDisabledComponentsWatched)
                 && Objects.equals(mEnabledComponentsWatched, that.mEnabledComponentsWatched)
                 && mCeDataInode == that.mCeDataInode
+                && mDeDataInode == that.mDeDataInode
                 && mDistractionFlags == that.mDistractionFlags
                 && mEnabledState == that.mEnabledState
                 && mInstallReason == that.mInstallReason
@@ -878,6 +890,7 @@
         _hash = 31 * _hash + Objects.hashCode(mDisabledComponentsWatched);
         _hash = 31 * _hash + Objects.hashCode(mEnabledComponentsWatched);
         _hash = 31 * _hash + Long.hashCode(mCeDataInode);
+        _hash = 31 * _hash + Long.hashCode(mDeDataInode);
         _hash = 31 * _hash + mDistractionFlags;
         _hash = 31 * _hash + mEnabledState;
         _hash = 31 * _hash + mInstallReason;
@@ -898,10 +911,10 @@
     }
 
     @DataClass.Generated(
-            time = 1691601685901L,
+            time = 1694196888631L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
-            inputSignatures = "private  int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate  boolean watchableEquals(com.android.server.utils.Watchable)\nprivate  int watchableHashCode()\nprivate  boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate  int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final  int INSTALLED\nprivate static final  int STOPPED\nprivate static final  int NOT_LAUNCHED\nprivate static final  int HIDDEN\nprivate static final  int INSTANT_APP\nprivate static final  int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+            inputSignatures = "private  int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  long mDeDataInode\nprivate  int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate  boolean watchableEquals(com.android.server.utils.Watchable)\nprivate  int watchableHashCode()\nprivate  boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate  int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final  int INSTALLED\nprivate static final  int STOPPED\nprivate static final  int NOT_LAUNCHED\nprivate static final  int HIDDEN\nprivate static final  int INSTANT_APP\nprivate static final  int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 812e228..f14941b 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -28,6 +28,7 @@
 import static android.os.Build.VERSION_CODES.DONUT;
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
 import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
 
 import android.annotation.AnyRes;
@@ -509,24 +510,33 @@
                 /* Set the global "on SD card" flag */
                 .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
 
+        var archivedPackage = lite.getArchivedPackage();
+        if (archivedPackage == null) {
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "archivePackage is missing");
+        }
+
         // parseBaseAppBasicFlags
         pkg
                 // Default true
-                .setBackupAllowed(lite.isBackupAllowed())
+                .setBackupAllowed(true)
                 .setClearUserDataAllowed(true)
-                .setClearUserDataOnFailedRestoreAllowed(
-                        lite.isClearUserDataOnFailedRestoreAllowed())
+                .setClearUserDataOnFailedRestoreAllowed(true)
                 .setAllowNativeHeapPointerTagging(true)
                 .setEnabled(true)
                 .setExtractNativeLibrariesRequested(true)
                 // targetSdkVersion gated
                 .setAllowAudioPlaybackCapture(targetSdk >= Build.VERSION_CODES.Q)
                 .setHardwareAccelerated(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-                .setRequestLegacyExternalStorage(lite.isRequestLegacyExternalStorage())
+                .setRequestLegacyExternalStorage(
+                        XmlUtils.convertValueToBoolean(archivedPackage.requestLegacyExternalStorage,
+                                targetSdk < Build.VERSION_CODES.Q))
                 .setCleartextTrafficAllowed(targetSdk < Build.VERSION_CODES.P)
                 // Default false
-                .setDefaultToDeviceProtectedStorage(lite.isDefaultToDeviceProtectedStorage())
-                .setUserDataFragile(lite.isUserDataFragile())
+                .setDefaultToDeviceProtectedStorage(XmlUtils.convertValueToBoolean(
+                        archivedPackage.defaultToDeviceProtectedStorage, false))
+                .setUserDataFragile(
+                        XmlUtils.convertValueToBoolean(archivedPackage.userDataFragile, false))
                 // Ints
                 .setCategory(ApplicationInfo.CATEGORY_UNDEFINED)
                 // Floats Default 0f
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4d38239..b001c6a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -438,7 +438,6 @@
     boolean mPreloadedRecentApps;
     final Object mServiceAcquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
-    SearchManager mSearchManager;
     AccessibilityManager mAccessibilityManager;
     AccessibilityManagerInternal mAccessibilityManagerInternal;
     BurnInProtectionHelper mBurnInProtectionHelper;
@@ -2217,7 +2216,6 @@
         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);
@@ -4009,8 +4007,9 @@
         args.putLong(Intent.EXTRA_TIME, eventTime);
         args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
 
-        if (mSearchManager != null) {
-            mSearchManager.launchAssist(args);
+        SearchManager searchManager = mContext.getSystemService(SearchManager.class);
+        if (searchManager != null) {
+            searchManager.launchAssist(args);
         } else {
             // Fallback to status bar if search manager doesn't exist (e.g. on wear).
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -4773,7 +4772,7 @@
             case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
-                if (down && mStylusButtonsEnabled) {
+                if (mStylusButtonsEnabled) {
                     sendSystemKeyToStatusBarAsync(event);
                 }
                 result &= ~ACTION_PASS_TO_USER;
diff --git a/services/core/java/com/android/server/security/TEST_MAPPING b/services/core/java/com/android/server/security/TEST_MAPPING
index 673456f..29d52ff 100644
--- a/services/core/java/com/android/server/security/TEST_MAPPING
+++ b/services/core/java/com/android/server/security/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-    "postsubmit": [
+    "presubmit": [
         {
             "name": "CtsSecurityTestCases",
             "options": [
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index c526016..a5c0fb3 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -1454,6 +1454,7 @@
         boolean hasDesiredDemuxCap = request.desiredFilterTypes
                 != DemuxFilterMainType.UNDEFINED;
         int smallestNumOfSupportedCaps = Integer.SIZE + 1;
+        int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
         for (DemuxResource dr : getDemuxResources().values()) {
             if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
                 if (!dr.isInUse()) {
@@ -1476,12 +1477,18 @@
                             currentLowestPriority = priority;
                             isRequestFromSameProcess = (requestClient.getProcessId()
                                 == (getClientProfile(dr.getOwnerClientId())).getProcessId());
+
+                            // reset the smallest caps when lower priority resource is found
+                            smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+
                             shouldUpdate = true;
-                        }
-                        // update smallest caps
-                        if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
-                            smallestNumOfSupportedCaps = numOfSupportedCaps;
-                            shouldUpdate = true;
+                        } else {
+                            // This is the case when the priority is the same as previously found
+                            // one. Update smallest caps when priority.
+                            if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
+                                smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+                                shouldUpdate = true;
+                            }
                         }
                         if (shouldUpdate) {
                             inUseLowestPriorityDrHandle = dr.getHandle();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3c2d0fc..4c525e9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -256,6 +256,15 @@
                     || event != CLOSE_WRITE // includes the MOVED_TO case
                     || wallpaper.imageWallpaperPending;
 
+            if (isMigration) {
+                // When separate lock screen engine is supported, migration will be handled by
+                // WallpaperDestinationChangeHandler.
+                return;
+            }
+            if (!(sysWallpaperChanged || lockWallpaperChanged)) {
+                return;
+            }
+
             if (DEBUG) {
                 Slog.v(TAG, "Wallpaper file change: evt=" + event
                         + " path=" + path
@@ -270,15 +279,6 @@
                         + " needsUpdate=" + needsUpdate);
             }
 
-            if (isMigration) {
-                // When separate lock screen engine is supported, migration will be handled by
-                // WallpaperDestinationChangeHandler.
-                return;
-            }
-            if (!(sysWallpaperChanged || lockWallpaperChanged)) {
-                return;
-            }
-
             int notifyColorsWhich = 0;
             synchronized (mLock) {
                 notifyCallbacksLocked(wallpaper);
@@ -3494,15 +3494,24 @@
         }
     }
 
+    /**
+     * Determines if the given component name is the default component. Note: a null name can be
+     * used to represent the default component.
+     * @param name The component name to check.
+     * @return True if the component name matches the default wallpaper component.
+     */
+    private boolean isDefaultComponent(ComponentName name) {
+        return name == null || name.equals(mDefaultWallpaperComponent);
+    }
+
     private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
         if (wallpaper.connection != null) {
-            if (wallpaper.wallpaperComponent == null) {
-                if (componentName == null) {
-                    if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
-                    // Still using default wallpaper.
-                    return true;
-                }
-            } else if (wallpaper.wallpaperComponent.equals(componentName)) {
+            final ComponentName wallpaperName = wallpaper.wallpaperComponent;
+            if (isDefaultComponent(componentName) && isDefaultComponent(wallpaperName)) {
+                if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
+                // Still using default wallpaper.
+                return true;
+            } else if (wallpaperName != null && wallpaperName.equals(componentName)) {
                 // Changing to same wallpaper.
                 if (DEBUG) Slog.v(TAG, "same wallpaper");
                 return true;
@@ -3519,6 +3528,9 @@
         // Has the component changed?
         if (!force && changingToSame(componentName, wallpaper)) {
             try {
+                if (DEBUG_LIVE) {
+                    Slog.v(TAG, "Changing to the same component, ignoring");
+                }
                 if (reply != null) reply.sendResult(null);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to send callback", e);
@@ -3737,7 +3749,11 @@
             mContext.getMainThreadHandler().removeCallbacks(
                     wallpaper.connection.mTryToRebindRunnable);
 
-            mContext.unbindService(wallpaper.connection);
+            try {
+                mContext.unbindService(wallpaper.connection);
+            } catch (IllegalArgumentException e) {
+                Slog.w(TAG, "Error unbinding wallpaper when detaching", e);
+            }
             wallpaper.connection = null;
             if (wallpaper == mLastWallpaper) {
                 mLastWallpaper = null;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 4c9ec9d..3c8e630 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1018,8 +1018,7 @@
         }
 
         try {
-            final ClientTransaction transaction = ClientTransaction.obtain(
-                    r.app.getThread(), r.token);
+            final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
             transaction.addCallback(EnterPipRequestedItem.obtain(r.token));
             mService.getLifecycleManager().scheduleTransaction(transaction);
             return true;
@@ -1040,8 +1039,7 @@
         }
 
         try {
-            final ClientTransaction transaction = ClientTransaction.obtain(
-                    r.app.getThread(), r.token);
+            final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
             transaction.addCallback(PipStateTransactionItem.obtain(r.token, pipState));
             mService.getLifecycleManager().scheduleTransaction(transaction);
         } catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dca2b6f..d824534 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -565,8 +565,8 @@
     boolean forceNewConfig; // force re-create with new config next time
     boolean supportsEnterPipOnTaskSwitch;  // This flag is set by the system to indicate that the
         // activity can enter picture in picture while pausing (only when switching to another task)
+    // The PiP params used when deferring the entering of picture-in-picture.
     PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
-        // The PiP params used when deferring the entering of picture-in-picture.
     boolean shouldDockBigOverlays;
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
@@ -580,7 +580,7 @@
     IBinder mRequestedLaunchingTaskFragmentToken;
 
     // Tracking splash screen status from previous activity
-    boolean mSplashScreenStyleSolidColor = false;
+    boolean mAllowIconSplashScreen = true;
 
     boolean mPauseSchedulePendingForPip = false;
 
@@ -1442,7 +1442,7 @@
                     + "display, activityRecord=%s, displayId=%d, config=%s", this, displayId,
                     config);
 
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                     MoveToDisplayItem.obtain(token, displayId, config));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -1459,7 +1459,7 @@
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
                     + "config: %s", this, config);
 
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                     ActivityConfigurationChangeItem.obtain(token, config));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -1480,7 +1480,7 @@
             ProtoLog.v(WM_DEBUG_STATES, "Sending position change to %s, onTop: %b",
                     this, onTop);
 
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                     TopResumedActivityChangeItem.obtain(token, onTop));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -1732,6 +1732,16 @@
         mAnimatingActivityRegistry = registry;
     }
 
+    boolean canAutoEnterPip() {
+        // beforeStopping=false since the actual pip-ing will take place after startPausing()
+        final boolean activityCanPip = checkEnterPictureInPictureState(
+                "startActivityUnchecked", false /* beforeStopping */);
+
+        // check if this activity is about to auto-enter pip
+        return activityCanPip && pictureInPictureArgs != null
+                && pictureInPictureArgs.isAutoEnterEnabled();
+    }
+
     /**
      * Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
      * {@link #getTask()} is set before this is called.
@@ -2398,8 +2408,7 @@
     @VisibleForTesting
     boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,
             boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,
-            boolean activityCreated, boolean isSimple,
-            boolean activityAllDrawn) {
+            boolean activityCreated, boolean allowIcon, boolean activityAllDrawn) {
         // If the display is frozen, we won't do anything until the actual window is
         // displayed so there is no reason to put in the starting window.
         if (!okToDisplay()) {
@@ -2434,8 +2443,8 @@
 
         final int typeParameter = StartingSurfaceController
                 .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
-                        allowTaskSnapshot, activityCreated, isSimple, useLegacy, activityAllDrawn,
-                        type, packageName, mUserId);
+                        allowTaskSnapshot, activityCreated, allowIcon, useLegacy,
+                        activityAllDrawn, type, packageName, mUserId);
 
         if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
             if (isActivityTypeHome()) {
@@ -2727,7 +2736,7 @@
         }
         try {
             mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                     TransferSplashScreenViewStateItem.obtain(token, parcelable,
                             windowAnimationLeash));
             scheduleTransferSplashScreenTimeout();
@@ -3894,7 +3903,7 @@
 
             try {
                 if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
-                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                         DestroyActivityItem.obtain(token, finishing, configChangeFlags));
             } catch (Exception e) {
                 // We can just ignore exceptions here...  if the process has crashed, our death
@@ -4800,9 +4809,9 @@
 
         if (isState(RESUMED) && attachedToProcess()) {
             try {
-                final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+                final ArrayList<ResultInfo> list = new ArrayList<>();
                 list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
-                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                         ActivityResultItem.obtain(token, list));
                 return;
             } catch (Exception e) {
@@ -4813,7 +4822,7 @@
         // Schedule sending results now for Media Projection setup.
         if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED,
                 STOPPING, STOPPED)) {
-            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
+            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread());
             // Build result to be returned immediately.
             transaction.addCallback(ActivityResultItem.obtain(
                     token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data))));
@@ -4908,7 +4917,7 @@
                 // Making sure the client state is RESUMED after transaction completed and doing
                 // so only if activity is currently RESUMED. Otherwise, client may have extra
                 // life-cycle calls to RESUMED (and PAUSED later).
-                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                         NewIntentItem.obtain(token, ar, mState == RESUMED));
                 unsent = false;
             } catch (RemoteException e) {
@@ -6143,7 +6152,7 @@
             EventLogTags.writeWmPauseActivity(mUserId, System.identityHashCode(this),
                     shortComponentName, "userLeaving=false", "make-active");
             try {
-                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                         PauseActivityItem.obtain(token, finishing, false /* userLeaving */,
                                 configChangeFlags, false /* dontReport */, mAutoEnteringPip));
             } catch (Exception e) {
@@ -6156,7 +6165,7 @@
             setState(STARTED, "makeActiveIfNeeded");
 
             try {
-                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                         StartActivityItem.obtain(token, takeOptions()));
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
@@ -6454,7 +6463,7 @@
             }
             EventLogTags.writeWmStopActivity(
                     mUserId, System.identityHashCode(this), shortComponentName);
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                     StopActivityItem.obtain(token, configChangeFlags));
 
             mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
@@ -6737,7 +6746,7 @@
     void onFirstWindowDrawn(WindowState win) {
         firstWindowDrawn = true;
         // stop tracking
-        mSplashScreenStyleSolidColor = true;
+        mAllowIconSplashScreen = false;
 
         if (mStartingWindow != null) {
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
@@ -6786,7 +6795,7 @@
     void onStartingWindowDrawn() {
         boolean wasTaskVisible = false;
         if (task != null) {
-            mSplashScreenStyleSolidColor = true;
+            mAllowIconSplashScreen = false;
             wasTaskVisible = !setTaskHasBeenVisible();
         }
 
@@ -7309,52 +7318,69 @@
         }
         return false;
     }
-    private boolean shouldUseSolidColorSplashScreen(ActivityRecord sourceRecord,
+
+    /**
+     * Checks whether an icon splash screen can be used in the starting window based on the
+     * preference in the {@code options} and this activity's theme, giving higher priority to the
+     * {@code options}'s preference.
+     *
+     * When no preference is specified, a default behaviour is defined:
+     *  - if the activity is started from the home or shell app, an icon can be used
+     *  - if the activity is started from SystemUI, an icon should not be used
+     *  - if there is a launching activity, use its preference
+     *  - if none of the above is met, only use an icon when the activity is started for the first
+     *    time from a System app
+     *
+     * The returned value is sent to WmShell, which will make the final decision on what splash
+     * screen type will be used.
+     *
+     * @return true if an icon can be used in the splash screen
+     *         false when an icon should not be used in the splash screen
+     */
+    private boolean canUseIconSplashScreen(ActivityRecord sourceRecord,
             boolean startActivity, ActivityOptions options, int resolvedTheme) {
         if (sourceRecord == null && !startActivity) {
-            // Use simple style if this activity is not top activity. This could happen when adding
-            // a splash screen window to the warm start activity which is re-create because top is
-            // finishing.
+            // Shouldn't use an icon if this activity is not top activity. This could happen when
+            // adding a splash screen window to the warm start activity which is re-create because
+            // top is finishing.
             final ActivityRecord above = task.getActivityAbove(this);
             if (above != null) {
-                return true;
+                return false;
             }
         }
 
         // setSplashScreenStyle decide in priority of windowSplashScreenBehavior.
-        if (options != null) {
-            final int optionsStyle = options.getSplashScreenStyle();
-            if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
-                return true;
-            } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
-                    || isIconStylePreferred(resolvedTheme)) {
-                return false;
-            }
-            // Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
-            // not specified in the ActivityOptions.
-            if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
-                    || launchedFromUid == Process.SHELL_UID) {
-                return false;
-            } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
-                return true;
-            }
-        } else if (isIconStylePreferred(resolvedTheme)) {
+        final int optionsStyle = options != null ? options.getSplashScreenStyle() :
+                SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
+        if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
             return false;
-        }
-        if (sourceRecord == null) {
-            sourceRecord = searchCandidateLaunchingActivity();
+        } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
+                    || isIconStylePreferred(resolvedTheme)) {
+            return true;
         }
 
-        if (sourceRecord != null && !sourceRecord.isActivityTypeHome()) {
-            return sourceRecord.mSplashScreenStyleSolidColor;
-        }
+        // Choose the default behavior when neither the ActivityRecord nor the activity theme have
+        // specified a splash screen style.
 
-        // If this activity was launched from Launcher or System for first start, never use a
-        // solid color splash screen.
-        // Need to check sourceRecord before in case this activity is launched from service.
-        return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
-                || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
-                || launchedFromUid == Process.SHELL_UID);
+        if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME || launchedFromUid == Process.SHELL_UID) {
+            return true;
+        } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+            return false;
+        } else {
+            // Need to check sourceRecord in case this activity is launched from a service or a
+            // trampoline activity.
+            if (sourceRecord == null) {
+                sourceRecord = searchCandidateLaunchingActivity();
+            }
+
+            if (sourceRecord != null) {
+                return sourceRecord.mAllowIconSplashScreen;
+            }
+
+            // Use an icon if the activity was launched from System for the first start.
+            // Otherwise, can't use an icon splash screen.
+            return mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM && startActivity;
+        }
     }
 
     private int getSplashscreenTheme(ActivityOptions options) {
@@ -7417,7 +7443,7 @@
         final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
                 splashScreenTheme);
 
-        mSplashScreenStyleSolidColor = shouldUseSolidColorSplashScreen(sourceRecord, startActivity,
+        mAllowIconSplashScreen = canUseIconSplashScreen(sourceRecord, startActivity,
                 startOptions, resolvedTheme);
 
         final boolean activityCreated =
@@ -7429,7 +7455,7 @@
 
         final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
                 prev, newTask || newSingleActivity, taskSwitch, processRunning,
-                allowTaskSnapshot(), activityCreated, mSplashScreenStyleSolidColor, allDrawn);
+                allowTaskSnapshot(), activityCreated, mAllowIconSplashScreen, allDrawn);
         if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
             Slog.d(TAG, "Scheduled starting window for " + this);
         }
@@ -9180,7 +9206,13 @@
                     getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED
                             ? newParentConfig.windowConfiguration.getWindowingMode()
                             : getRequestedOverrideWindowingMode();
-            if (getWindowingMode() != projectedWindowingMode) {
+            if (getWindowingMode() != projectedWindowingMode
+                    // Do not collect a pip activity about to enter pinned mode
+                    // as a part of WindowOrganizerController#finishTransition().
+                    // If not checked the activity might be collected for the wrong transition,
+                    // such as a TRANSIT_OPEN transition requested right after TRANSIT_PIP.
+                    && !(mWaitForEnteringPinnedMode
+                    && mTransitionController.inFinishingTransition(this))) {
                 mTransitionController.collect(this);
             }
         }
@@ -9869,7 +9901,6 @@
             ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
                     (andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
             forceNewConfig = false;
-            startRelaunching();
             final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token,
                     pendingResults, pendingNewIntents, configChangeFlags,
                     new MergedConfiguration(getProcessGlobalConfiguration(),
@@ -9882,15 +9913,16 @@
             } else {
                 lifecycleItem = PauseActivityItem.obtain(token);
             }
-            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
+            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread());
             transaction.addCallback(callbackItem);
             transaction.setLifecycleStateRequest(lifecycleItem);
             mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+            startRelaunching();
             // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
             // request resume if this activity is currently resumed, which implies we aren't
             // sleeping.
         } catch (RemoteException e) {
-            ProtoLog.i(WM_DEBUG_STATES, "Relaunch failed %s", e);
+            Slog.w(TAG, "Failed to relaunch " + this + ": " + e);
         }
 
         if (andResume) {
@@ -9976,7 +10008,7 @@
         // The process will be killed until the activity reports stopped with saved state (see
         // {@link ActivityTaskManagerService.activityStopped}).
         try {
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
                     StopActivityItem.obtain(token, 0 /* configChanges */));
         } catch (RemoteException e) {
             Slog.w(TAG, "Exception thrown during restart " + this, e);
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index a5b1132..25c42b4 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -33,9 +33,7 @@
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
@@ -240,11 +238,6 @@
                 getInterceptorInfo(null /* clearOptionsAnimation */);
 
         for (int i = 0; i < callbacks.size(); i++) {
-            final int orderId = callbacks.keyAt(i);
-            if (!shouldInterceptActivityLaunch(orderId, interceptorInfo)) {
-                continue;
-            }
-
             final ActivityInterceptorCallback callback = callbacks.valueAt(i);
             final ActivityInterceptResult interceptResult = callback.onInterceptActivityLaunch(
                     interceptorInfo);
@@ -543,11 +536,6 @@
         ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(
                 r::clearOptionsAnimationForSiblings);
         for (int i = 0; i < callbacks.size(); i++) {
-            final int orderId = callbacks.keyAt(i);
-            if (!shouldNotifyOnActivityLaunch(orderId, info)) {
-                continue;
-            }
-
             final ActivityInterceptorCallback callback = callbacks.valueAt(i);
             callback.onActivityLaunched(taskInfo, r.info, info);
         }
@@ -565,21 +553,4 @@
                 .build();
     }
 
-    private boolean shouldInterceptActivityLaunch(
-            @ActivityInterceptorCallback.OrderedId int orderId,
-            @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
-        if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
-            return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
-        }
-        return true;
-    }
-
-    private boolean shouldNotifyOnActivityLaunch(
-            @ActivityInterceptorCallback.OrderedId int orderId,
-            @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
-        if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
-            return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
-        }
-        return true;
-    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6999c6a..f38f6b0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -313,6 +313,8 @@
 public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
     private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
+    private static final String ENABLE_PIP2_IMPLEMENTATION =
+            "persist.wm.debug.enable_pip2_implementation";
     static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
     static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
 
@@ -1841,8 +1843,12 @@
             RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
         mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
                 "startBackNavigation()");
-
-        return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
     }
 
     /**
@@ -3613,6 +3619,28 @@
         }
     }
 
+    /**
+     * Prepare to enter PiP mode after {@link TransitionController#requestStartTransition}.
+     *
+     * @param r activity auto entering pip
+     * @return true if the activity is about to auto-enter pip or is already in pip mode.
+     */
+    boolean prepareAutoEnterPictureAndPictureMode(ActivityRecord r) {
+        // If the activity is already in picture in picture mode, then just return early
+        if (r.inPinnedWindowingMode()) {
+            return true;
+        }
+
+        if (r.canAutoEnterPip() && getTransitionController().getCollectingTransition() != null) {
+            // This will be used later to construct TransitionRequestInfo for Shell to resolve.
+            // It will also be passed into a direct moveActivityToPinnedRootTask() call via
+            // startTransition()
+            getTransitionController().getCollectingTransition().setPipActivity(r);
+            return true;
+        }
+        return false;
+    }
+
     boolean enterPictureInPictureMode(@NonNull ActivityRecord r,
             @NonNull PictureInPictureParams params, boolean fromClient) {
         return enterPictureInPictureMode(r, params, fromClient, false /* isAutoEnter */);
@@ -7183,4 +7211,8 @@
             ActivityTaskManagerService.this.unregisterTaskStackListener(listener);
         }
     }
+
+    static boolean isPip2ExperimentEnabled() {
+        return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e523119..1ad5798 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -795,17 +795,13 @@
             return false;
         }
 
-        // Try pausing the existing resumed activity in the same TaskFragment if any.
-        final TaskFragment taskFragment = r.getTaskFragment();
-        if (taskFragment != null && taskFragment.getResumedActivity() != null) {
-            if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) {
-                return false;
-            }
+        // Try pausing the existing resumed activity in the Task if any.
+        final Task task = r.getTask();
+        if (task.pauseActivityIfNeeded(r, "realStart")) {
+            return false;
         }
 
-        final Task task = r.getTask();
         final Task rootTask = task.getRootTask();
-
         beginDeferResume();
         // The LaunchActivityItem also contains process configuration, so the configuration change
         // from WindowProcessController#setProcess can be deferred. The major reason is that if
@@ -923,7 +919,7 @@
 
                 // Create activity launch transaction.
                 final ClientTransaction clientTransaction = ClientTransaction.obtain(
-                        proc.getThread(), r.token);
+                        proc.getThread());
 
                 final boolean isTransitionForward = r.isTransitionForward();
                 final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 7430f0f..ef31837 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -18,11 +18,10 @@
 
 import android.annotation.NonNull;
 import android.app.IApplicationThread;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
-import android.app.servertransaction.ActivityLifecycleItem;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.RemoteException;
 
 /**
@@ -36,13 +35,13 @@
     // TODO(lifecycler): Use object pools for transactions and transaction items.
 
     /**
-     * Schedule a transaction, which may consist of multiple callbacks and a lifecycle request.
+     * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
      * @param transaction A sequence of client transaction items.
      * @throws RemoteException
      *
      * @see ClientTransaction
      */
-    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+    void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
         final IApplicationThread client = transaction.getClient();
         transaction.schedule();
         if (!(client instanceof Binder)) {
@@ -54,75 +53,22 @@
     }
 
     /**
-     * Schedule a single lifecycle request or callback to client activity.
+     * Schedules a single transaction item, either a callback or a lifecycle request, delivery to
+     * client application.
      * @param client Target client.
-     * @param activityToken Target activity token.
-     * @param stateRequest A request to move target activity to a desired lifecycle state.
-     * @throws RemoteException
-     *
-     * @see ClientTransactionItem
-     */
-    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
-            @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
-        final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
-                stateRequest);
-        scheduleTransaction(clientTransaction);
-    }
-
-    /**
-     * Schedule a single callback delivery to client activity.
-     * @param client Target client.
-     * @param activityToken Target activity token.
-     * @param callback A request to deliver a callback.
-     * @throws RemoteException
-     *
-     * @see ClientTransactionItem
-     */
-    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
-            @NonNull ClientTransactionItem callback) throws RemoteException {
-        final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,
-                callback);
-        scheduleTransaction(clientTransaction);
-    }
-
-    /**
-     * Schedule a single callback delivery to client application.
-     * @param client Target client.
-     * @param callback A request to deliver a callback.
+     * @param transactionItem A transaction item to deliver a message.
      * @throws RemoteException
      *
      * @see ClientTransactionItem
      */
     void scheduleTransaction(@NonNull IApplicationThread client,
-            @NonNull ClientTransactionItem callback) throws RemoteException {
-        final ClientTransaction clientTransaction = transactionWithCallback(client,
-                null /* activityToken */, callback);
+            @NonNull ClientTransactionItem transactionItem) throws RemoteException {
+        final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
+        if (transactionItem instanceof ActivityLifecycleItem) {
+            clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
+        } else {
+            clientTransaction.addCallback(transactionItem);
+        }
         scheduleTransaction(clientTransaction);
     }
-
-    /**
-     * @return A new instance of {@link ClientTransaction} with a single lifecycle state request.
-     *
-     * @see ClientTransaction
-     * @see ClientTransactionItem
-     */
-    private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
-            @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
-        clientTransaction.setLifecycleStateRequest(stateRequest);
-        return clientTransaction;
-    }
-
-    /**
-     * @return A new instance of {@link ClientTransaction} with a single callback invocation.
-     *
-     * @see ClientTransaction
-     * @see ClientTransactionItem
-     */
-    private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,
-            IBinder activityToken, @NonNull ClientTransactionItem callback) {
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
-        clientTransaction.addCallback(callback);
-        return clientTransaction;
-    }
 }
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 7cd07d6..b499dad 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -83,6 +83,11 @@
     @Nullable private Rect mLastRecordedBounds = null;
 
     /**
+     * The last size of the surface mirrored out to.
+     */
+    @Nullable private Point mLastConsumingSurfaceSize = new Point(0, 0);
+
+    /**
      * The last configuration orientation.
      */
     @Configuration.Orientation
@@ -141,60 +146,64 @@
      */
     void onConfigurationChanged(@Configuration.Orientation int lastOrientation) {
         // Update surface for MediaProjection, if this DisplayContent is being used for recording.
-        if (isCurrentlyRecording() && mLastRecordedBounds != null) {
-            // Recording has already begun, but update recording since the display is now on.
-            if (mRecordedWindowContainer == null) {
+        if (!isCurrentlyRecording() || mLastRecordedBounds == null) {
+            return;
+        }
+
+        // Recording has already begun, but update recording since the display is now on.
+        if (mRecordedWindowContainer == null) {
+            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                    "Content Recording: Unexpectedly null window container; unable to update "
+                            + "recording for display %d",
+                    mDisplayContent.getDisplayId());
+            return;
+        }
+
+        // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are
+        //  inaccurate.
+        if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+            final Task capturedTask = mRecordedWindowContainer.asTask();
+            if (capturedTask.inPinnedWindowingMode()) {
                 ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                        "Content Recording: Unexpectedly null window container; unable to update "
-                                + "recording for display %d",
+                        "Content Recording: Display %d was already recording, but "
+                                + "pause capture since the task is in PIP",
                         mDisplayContent.getDisplayId());
+                pauseRecording();
                 return;
             }
+        }
 
-            // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are
-            //  inaccurate.
-            if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
-                final Task capturedTask = mRecordedWindowContainer.asTask();
-                if (capturedTask.inPinnedWindowingMode()) {
-                    ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                            "Content Recording: Display %d was already recording, but "
-                                    + "pause capture since the task is in PIP",
-                            mDisplayContent.getDisplayId());
-                    pauseRecording();
-                    return;
-                }
-            }
-
-            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                    "Content Recording: Display %d was already recording, so apply "
-                            + "transformations if necessary",
-                    mDisplayContent.getDisplayId());
-            // Retrieve the size of the region to record, and continue with the update
-            // if the bounds or orientation has changed.
-            final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
-            @Configuration.Orientation int recordedContentOrientation =
-                    mRecordedWindowContainer.getConfiguration().orientation;
-            if (!mLastRecordedBounds.equals(recordedContentBounds)
-                    || lastOrientation != recordedContentOrientation) {
-                Point surfaceSize = fetchSurfaceSizeIfPresent();
-                if (surfaceSize != null) {
-                    ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                            "Content Recording: Going ahead with updating recording for display "
-                                    + "%d to new bounds %s and/or orientation %d.",
-                            mDisplayContent.getDisplayId(), recordedContentBounds,
-                            recordedContentOrientation);
-                    updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
-                            recordedContentBounds, surfaceSize);
-                } else {
-                    // If the surface removed, do nothing. We will handle this via onDisplayChanged
-                    // (the display will be off if the surface is removed).
-                    ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                            "Content Recording: Unable to update recording for display %d to new "
-                                    + "bounds %s and/or orientation %d, since the surface is not "
-                                    + "available.",
-                            mDisplayContent.getDisplayId(), recordedContentBounds,
-                            recordedContentOrientation);
-                }
+        ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                "Content Recording: Display %d was already recording, so apply "
+                        + "transformations if necessary",
+                mDisplayContent.getDisplayId());
+        // Retrieve the size of the region to record, and continue with the update
+        // if the bounds or orientation has changed.
+        final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
+        @Configuration.Orientation int recordedContentOrientation =
+                mRecordedWindowContainer.getConfiguration().orientation;
+        final Point surfaceSize = fetchSurfaceSizeIfPresent();
+        if (!mLastRecordedBounds.equals(recordedContentBounds)
+                || lastOrientation != recordedContentOrientation
+                || !mLastConsumingSurfaceSize.equals(surfaceSize)) {
+            if (surfaceSize != null) {
+                ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                        "Content Recording: Going ahead with updating recording for display "
+                                + "%d to new bounds %s and/or orientation %d and/or surface "
+                                + "size %s",
+                        mDisplayContent.getDisplayId(), recordedContentBounds,
+                        recordedContentOrientation, surfaceSize);
+                updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
+                        recordedContentBounds, surfaceSize);
+            } else {
+                // If the surface removed, do nothing. We will handle this via onDisplayChanged
+                // (the display will be off if the surface is removed).
+                ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                        "Content Recording: Unable to update recording for display %d to new "
+                                + "bounds %s and/or orientation %d and/or surface size %s, "
+                                + "since the surface is not available.",
+                        mDisplayContent.getDisplayId(), recordedContentBounds,
+                        recordedContentOrientation, surfaceSize);
             }
         }
     }
@@ -498,10 +507,13 @@
         }
 
         ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                "Content Recording: Apply transformations of shift %d x %d, scale %f, crop %d x "
-                        + "%d for display %d",
+                "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka "
+                        + "recorded content size) %d x %d for display %d; display has size %d x "
+                        + "%d; surface has size %d x %d",
                 shiftedX, shiftedY, scale, recordedContentBounds.width(),
-                recordedContentBounds.height(), mDisplayContent.getDisplayId());
+                recordedContentBounds.height(), mDisplayContent.getDisplayId(),
+                mDisplayContent.getConfiguration().screenWidthDp,
+                mDisplayContent.getConfiguration().screenHeightDp, surfaceSize.x, surfaceSize.y);
 
         transaction
                 // Crop the area to capture to exclude the 'extra' wallpaper that is used
@@ -515,6 +527,8 @@
                 // the content will no longer be centered in the output surface.
                 .setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */);
         mLastRecordedBounds = new Rect(recordedContentBounds);
+        mLastConsumingSurfaceSize.x = surfaceSize.x;
+        mLastConsumingSurfaceSize.y = surfaceSize.y;
         // Request to notify the client about the resize.
         mMediaProjectionManager.notifyActiveProjectionCapturedContentResized(
                 mLastRecordedBounds.width(), mLastRecordedBounds.height());
@@ -523,6 +537,7 @@
     /**
      * Returns a non-null {@link Point} if the surface is present, or null otherwise
      */
+    @Nullable
     private Point fetchSurfaceSizeIfPresent() {
         // Retrieve the default size of the surface the app provided to
         // MediaProjection#createVirtualDisplay. Note the app is the consumer of the surface,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 996d666..4309e72 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6040,8 +6040,9 @@
                 mOffTokenAcquirer.release(mDisplayId);
             }
             ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                    "Content Recording: Display %d state is now (%d), so update recording?",
-                    mDisplayId, displayState);
+                    "Content Recording: Display %d state was (%d), is now (%d), so update "
+                            + "recording?",
+                    mDisplayId, lastDisplayState, displayState);
             if (lastDisplayState != displayState) {
                 // If state is on due to surface being added, then start recording.
                 // If state is off due to surface being removed, then stop recording.
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index c4ed0dd..534cdc2 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -227,7 +227,7 @@
                     "Refreshing activity for camera compatibility treatment, "
                             + "activityRecord=%s", activity);
             final ClientTransaction transaction = ClientTransaction.obtain(
-                    activity.app.getThread(), activity.token);
+                    activity.app.getThread());
             transaction.addCallback(RefreshCallbackItem.obtain(activity.token,
                             cycleThroughStop ? ON_STOP : ON_PAUSE));
             transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(activity.token,
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 8519e4d..0f1a105 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -21,6 +21,7 @@
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
@@ -32,6 +33,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.MY_PID;
 import static com.android.server.wm.WindowManagerService.MY_UID;
+
 import static java.util.concurrent.CompletableFuture.completedFuture;
 
 import android.animation.Animator;
@@ -46,6 +48,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
+import android.os.InputConfig;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -183,13 +186,8 @@
         // Crop the input surface to the display size.
         mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
 
-        // Make trusted overlay to not block any touches while D&D ongoing and allowing
-        // touches to pass through to windows underneath. This allows user to interact with the
-        // UI to navigate while dragging.
-        h.setTrustedOverlay(mTransaction, mInputSurface, true);
         mTransaction.show(mInputSurface)
                 .setInputWindowInfo(mInputSurface, h)
-                .setTrustedOverlay(mInputSurface, true)
                 .setLayer(mInputSurface, Integer.MAX_VALUE)
                 .setCrop(mInputSurface, mTmpClipRect);
 
@@ -379,6 +377,11 @@
             mDragWindowHandle.ownerUid = MY_UID;
             mDragWindowHandle.scaleFactor = 1.0f;
 
+            // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing
+            // touches to pass through to windows underneath. This allows user to interact with the
+            // UI to navigate while dragging.
+            mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY;
+
             // The drag window cannot receive new touches.
             mDragWindowHandle.touchableRegion.setEmpty();
 
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index c21930d..39622c1 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -74,7 +74,7 @@
         mWindowHandle.ownerPid = WindowManagerService.MY_PID;
         mWindowHandle.ownerUid = WindowManagerService.MY_UID;
         mWindowHandle.scaleFactor = 1.0f;
-        mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
+        mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
 
         mInputSurface = mService.makeSurfaceBuilder(
                         mService.mRoot.getDisplayContent(displayId).getSession())
@@ -129,14 +129,12 @@
 
     void show(SurfaceControl.Transaction t, WindowContainer w) {
         t.show(mInputSurface);
-        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
     }
 
     void show(SurfaceControl.Transaction t, int layer) {
         t.show(mInputSurface);
-        mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, layer);
     }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f77da62..825d38b 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,10 +40,12 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
+
 import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.Nullable;
@@ -730,7 +732,7 @@
                 new InputWindowHandle(null /* inputApplicationHandle */, displayId));
         inputWindowHandle.setName(name);
         inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
-        inputWindowHandle.setTrustedOverlay(t, sc, true);
+        inputWindowHandle.setTrustedOverlay(true);
         populateOverlayInputInfo(inputWindowHandle);
         setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
     }
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 90d81bd..64b7a60 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -195,11 +195,6 @@
         mChanged = true;
     }
 
-    void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
-            boolean trustedOverlay) {
-        mHandle.setTrustedOverlay(t, sc, trustedOverlay);
-    }
-
     void setOwnerPid(int pid) {
         if (mHandle.ownerPid == pid) {
             return;
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 579fd14..45cf10b 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -29,7 +29,6 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.FeatureFlags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -91,6 +90,13 @@
             "enable_app_compat_user_aspect_ratio_fullscreen";
     private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true;
 
+    // Whether the letterbox wallpaper style is enabled by default
+    private static final String KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER =
+            "enable_letterbox_background_wallpaper";
+
+    // TODO(b/290048978): Enable wallpaper as default letterbox background.
+    private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = false;
+
     /**
      * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
      * set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -179,9 +185,6 @@
     @NonNull
     private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
 
-    @NonNull
-    private final FeatureFlags mFeatureFlags;
-
     // Aspect ratio of letterbox for fixed orientation, values <=
     // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
     private float mFixedOrientationLetterboxAspectRatio;
@@ -298,8 +301,7 @@
     // Flags dynamically updated with {@link android.provider.DeviceConfig}.
     @NonNull private final SynchedDeviceConfig mDeviceConfig;
 
-    LetterboxConfiguration(@NonNull final Context systemUiContext,
-                           @NonNull FeatureFlags featureFags) {
+    LetterboxConfiguration(@NonNull final Context systemUiContext) {
         this(systemUiContext, new LetterboxConfigurationPersister(
                 () -> readLetterboxHorizontalReachabilityPositionFromConfig(
                         systemUiContext, /* forBookMode */ false),
@@ -308,16 +310,14 @@
                 () -> readLetterboxHorizontalReachabilityPositionFromConfig(
                         systemUiContext, /* forBookMode */ true),
                 () -> readLetterboxVerticalReachabilityPositionFromConfig(
-                        systemUiContext, /* forTabletopMode */ true)),
-                featureFags);
+                        systemUiContext, /* forTabletopMode */ true)));
     }
 
     @VisibleForTesting
     LetterboxConfiguration(@NonNull final Context systemUiContext,
-            @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister,
-            @NonNull FeatureFlags featureFags) {
+            @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister) {
         mContext = systemUiContext;
-        mFeatureFlags = featureFags;
+
         mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
                 R.dimen.config_fixedOrientationLetterboxAspectRatio);
         mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
@@ -385,6 +385,8 @@
                         DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS,
                         mContext.getResources().getBoolean(
                                 R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled))
+                .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER,
+                        DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, /* enabled */ true)
                 .addDeviceConfigEntry(KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN,
                         DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN,
                         mContext.getResources().getBoolean(
@@ -542,7 +544,8 @@
     }
 
     /**
-     * Resets letterbox background type value depending on the built time and runtime flags.
+     * Resets letterbox background type value depending on the
+     * {@link #KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER} built time and runtime flags.
      *
      * <p>If enabled, the letterbox background type value is set toZ
      * {@link #LETTERBOX_BACKGROUND_WALLPAPER}. When disabled the letterbox background type value
@@ -552,11 +555,12 @@
         mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET;
     }
 
-    // Returns LETTERBOX_BACKGROUND_WALLPAPER if the flag is enabled or the value in
-    // com.android.internal.R.integer.config_letterboxBackgroundType if the flag is disabled.
+    // Returns KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER if the DeviceConfig flag is enabled
+    // or the value in com.android.internal.R.integer.config_letterboxBackgroundType if the flag
+    // is disabled.
     @LetterboxBackgroundType
     private int getDefaultLetterboxBackgroundType() {
-        return mFeatureFlags.letterboxBackgroundWallpaperFlag()
+        return mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER)
                 ? LETTERBOX_BACKGROUND_WALLPAPER : mLetterboxBackgroundType;
     }
 
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 01786be..735cbc4 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -33,6 +33,7 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -661,6 +662,10 @@
     @ScreenOrientation
     int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
         if (shouldApplyUserFullscreenOverride()) {
+            Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+                    + mActivityRecord + " is overridden to "
+                    + screenOrientationToString(SCREEN_ORIENTATION_USER)
+                    + " by user aspect ratio settings.");
             return SCREEN_ORIENTATION_USER;
         }
 
@@ -668,6 +673,15 @@
         // orientation.
         candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
 
+        if (shouldApplyUserMinAspectRatioOverride() && (!isFixedOrientation(candidate)
+                || candidate == SCREEN_ORIENTATION_LOCKED)) {
+            Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+                    + mActivityRecord + " is overridden to "
+                    + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT)
+                    + " by user aspect ratio settings.");
+            return SCREEN_ORIENTATION_PORTRAIT;
+        }
+
         if (FALSE.equals(mBooleanPropertyAllowOrientationOverride)) {
             return candidate;
         }
@@ -1126,11 +1140,25 @@
         return computeAspectRatio(bounds);
     }
 
+    /**
+     * Whether we should enable users to resize the current app.
+     */
+    boolean shouldEnableUserAspectRatioSettings() {
+        // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+        // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+        // the current app doesn't opt-out so the first part of the predicate is true.
+        return !FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
+                && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+                && mActivityRecord.mDisplayContent != null
+                && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
+    }
+
+    /**
+     * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+     * current app.
+     */
     boolean shouldApplyUserMinAspectRatioOverride() {
-        if (FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
-                || !mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
-                || mActivityRecord.mDisplayContent == null
-                || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+        if (!shouldEnableUserAspectRatioSettings()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index be90588..ee05e35 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -117,6 +118,11 @@
                 return;
             }
             if (targetActivity.attachedToProcess()) {
+                if (targetActivity.app.getCurrentProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
+                    Slog.v(TAG, "Skip preload recents for cached proc " + targetActivity.app);
+                    // The process may be frozen that cannot receive binder call.
+                    return;
+                }
                 // The activity may be relaunched if it cannot handle the current configuration
                 // changes. The activity will be paused state if it is relaunched, otherwise it
                 // keeps the original stopped state.
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index bbb8563..2c142cb 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -40,11 +40,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
-import android.os.IBinder;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayAddress;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
@@ -57,7 +55,6 @@
 import com.android.internal.R;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.protolog.common.ProtoLog;
-import com.android.server.display.DisplayControl;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
@@ -171,32 +168,10 @@
         try {
             final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
             if (isSizeChanged) {
-                final DisplayAddress address = displayInfo.address;
-                if (!(address instanceof DisplayAddress.Physical)) {
-                    Slog.e(TAG, "Display does not have a physical address: " + displayId);
-                    return;
-                }
-                final DisplayAddress.Physical physicalAddress =
-                        (DisplayAddress.Physical) address;
-                final IBinder displayToken = DisplayControl.getPhysicalDisplayToken(
-                        physicalAddress.getPhysicalDisplayId());
-                if (displayToken == null) {
-                    Slog.e(TAG, "Display token is null.");
-                    return;
-                }
                 // Temporarily not skip screenshot for the rounded corner overlays and screenshot
                 // the whole display to include the rounded corner overlays.
                 setSkipScreenshotForRoundedCornerOverlays(false, t);
-                mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays();
-                final ScreenCapture.DisplayCaptureArgs captureArgs =
-                        new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
-                                .setSourceCrop(new Rect(0, 0, width, height))
-                                .setAllowProtected(true)
-                                .setCaptureSecureLayers(true)
-                                .setHintForSeamlessTransition(true)
-                                .build();
-                screenshotBuffer = ScreenCapture.captureDisplay(captureArgs);
-            } else {
+            }
                 ScreenCapture.LayerCaptureArgs captureArgs =
                         new ScreenCapture.LayerCaptureArgs.Builder(
                                 displayContent.getSurfaceControl())
@@ -206,7 +181,6 @@
                                 .setHintForSeamlessTransition(true)
                                 .build();
                 screenshotBuffer = ScreenCapture.captureLayers(captureArgs);
-            }
 
             if (screenshotBuffer == null) {
                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a55c232..a0517be 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -19,12 +19,12 @@
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
 
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT;
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -102,7 +102,7 @@
 
     static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
             boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
-            boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType,
+            boolean allowIcon, boolean useLegacy, boolean activityDrawn, int startingWindowType,
             String packageName, int userId) {
         int parameter = 0;
         if (newTask) {
@@ -120,8 +120,8 @@
         if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) {
             parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
         }
-        if (isSolidColor) {
-            parameter |= TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+        if (allowIcon) {
+            parameter |= TYPE_PARAMETER_ALLOW_ICON;
         }
         if (useLegacy) {
             parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 21526e7..b4b8a74 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1262,6 +1262,37 @@
         return null;
     }
 
+    boolean pauseActivityIfNeeded(@Nullable ActivityRecord resuming, @NonNull String reason) {
+        if (!isLeafTask()) {
+            return false;
+        }
+
+        final int[] someActivityPaused = {0};
+        // Check if the direct child resumed activity in the leaf task needed to be paused if
+        // the leaf task is not a leaf task fragment.
+        if (!isLeafTaskFragment()) {
+            final ActivityRecord top = topRunningActivity();
+            final ActivityRecord resumedActivity = getResumedActivity();
+            if (resumedActivity != null && top.getTaskFragment() != this) {
+                // Pausing the resumed activity because it is occluded by other task fragment.
+                if (startPausing(false /* uiSleeping*/, resuming, reason)) {
+                    someActivityPaused[0]++;
+                }
+            }
+        }
+
+        forAllLeafTaskFragments((taskFrag) -> {
+            final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+            if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
+                if (taskFrag.startPausing(false /* uiSleeping*/, resuming, reason)) {
+                    someActivityPaused[0]++;
+                }
+            }
+        }, true /* traverseTopToBottom */);
+
+        return someActivityPaused[0] > 0;
+    }
+
     void updateTaskMovement(boolean toTop, boolean toBottom, int position) {
         EventLogTags.writeWmTaskMoved(mTaskId, getRootTaskId(), getDisplayId(), toTop ? 1 : 0,
                 position);
@@ -3478,9 +3509,9 @@
             }
         }
         // User Aspect Ratio Settings is enabled if the app is not in SCM
-        info.topActivityEligibleForUserAspectRatioButton =
-                mWmService.mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
-                        && top != null && !info.topActivityInSizeCompat;
+        info.topActivityEligibleForUserAspectRatioButton = top != null
+                && !info.topActivityInSizeCompat
+                && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
         info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
     }
 
@@ -5102,7 +5133,6 @@
 
     void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
             boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
-        final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
         final boolean isOrhasTask = rTask == this || hasChild(rTask);
@@ -5166,8 +5196,10 @@
             // supporting picture-in-picture while pausing only if the starting activity
             // would not be considered an overlay on top of the current activity
             // (eg. not fullscreen, or the assistant)
-            enableEnterPipOnTaskSwitch(pipCandidate,
-                    null /* toFrontTask */, r, options);
+            if (!ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+                final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
+                enableEnterPipOnTaskSwitch(pipCandidate, null /* toFrontTask */, r, options);
+            }
         }
         boolean doShow = true;
         if (newTask) {
@@ -5240,7 +5272,7 @@
      * enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
      * {@param toFrontActivity} should be set.
      */
-    private static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
+    static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
             @Nullable Task toFrontTask, @Nullable ActivityRecord toFrontActivity,
             @Nullable ActivityOptions opts) {
         if (pipCandidate == null) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9af12ad..ae794a8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1271,27 +1271,9 @@
     boolean pauseBackTasks(ActivityRecord resuming) {
         final int[] someActivityPaused = {0};
         forAllLeafTasks(leafTask -> {
-            // Check if the direct child resumed activity in the leaf task needed to be paused if
-            // the leaf task is not a leaf task fragment.
-            if (!leafTask.isLeafTaskFragment()) {
-                final ActivityRecord top = topRunningActivity();
-                final ActivityRecord resumedActivity = leafTask.getResumedActivity();
-                if (resumedActivity != null && top.getTaskFragment() != leafTask) {
-                    // Pausing the resumed activity because it is occluded by other task fragment.
-                    if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
-                        someActivityPaused[0]++;
-                    }
-                }
+            if (leafTask.pauseActivityIfNeeded(resuming, "pauseBackTasks")) {
+                someActivityPaused[0]++;
             }
-
-            leafTask.forAllLeafTaskFragments((taskFrag) -> {
-                final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
-                if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
-                    if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
-                        someActivityPaused[0]++;
-                    }
-                }
-            }, true /* traverseTopToBottom */);
         }, true /* traverseTopToBottom */);
         return someActivityPaused[0] > 0;
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 57f44cb..5d95bc7 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1456,8 +1456,8 @@
             }
 
             try {
-                final ClientTransaction transaction =
-                        ClientTransaction.obtain(next.app.getThread(), next.token);
+                final ClientTransaction transaction = ClientTransaction.obtain(
+                        next.app.getThread());
                 // Deliver all pending results.
                 ArrayList<ResultInfo> a = next.results;
                 if (a != null) {
@@ -1643,7 +1643,17 @@
             // next activity.
             final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
                     "shouldAutoPipWhilePausing", userLeaving);
-            if (userLeaving && resumingOccludesParent && lastResumedCanPip
+
+            if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+                // If a new task is being launched, then mark the existing top activity as
+                // supporting picture-in-picture while pausing only if the starting activity
+                // would not be considered an overlay on top of the current activity
+                // (eg. not fullscreen, or the assistant)
+                Task.enableEnterPipOnTaskSwitch(prev, resuming.getTask(),
+                        resuming, resuming.getOptions());
+            }
+            if (prev.supportsEnterPipOnTaskSwitch && userLeaving
+                    && resumingOccludesParent && lastResumedCanPip
                     && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
                 shouldAutoPip = true;
             } else if (!lastResumedCanPip) {
@@ -1656,7 +1666,12 @@
         }
 
         if (prev.attachedToProcess()) {
-            if (shouldAutoPip) {
+            if (shouldAutoPip && ActivityTaskManagerService.isPip2ExperimentEnabled()) {
+                prev.mPauseSchedulePendingForPip = true;
+                boolean willAutoPip = mAtmService.prepareAutoEnterPictureAndPictureMode(prev);
+                ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, requesting PIP mode "
+                        + "via requestStartTransition(): %s, willAutoPip: %b", prev, willAutoPip);
+            } else if (shouldAutoPip) {
                 prev.mPauseSchedulePendingForPip = true;
                 boolean didAutoPip = mAtmService.enterPictureInPictureMode(
                         prev, prev.pictureInPictureArgs, false /* fromClient */);
@@ -1726,7 +1741,7 @@
                     prev.shortComponentName, "userLeaving=" + userLeaving, reason);
 
             mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
-                    prev.token, PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
+                    PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
                             prev.configChangeFlags, pauseImmediately, autoEnteringPip));
         } catch (Exception e) {
             // Ignore exception, if process died other code will cleanup.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 843e6d1..bbafa25 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -174,6 +174,8 @@
     private final Token mToken;
     private IApplicationThread mRemoteAnimApp;
 
+    private @Nullable ActivityRecord mPipActivity;
+
     /** Only use for clean-up after binder death! */
     private SurfaceControl.Transaction mStartTransaction = null;
     private SurfaceControl.Transaction mFinishTransaction = null;
@@ -510,6 +512,21 @@
     }
 
     /**
+     * Set the pip-able activity participating in this transition.
+     * @param pipActivity activity about to enter pip
+     */
+    void setPipActivity(@Nullable ActivityRecord pipActivity) {
+        mPipActivity = pipActivity;
+    }
+
+    /**
+     * @return pip-able activity participating in this transition.
+     */
+    @Nullable ActivityRecord getPipActivity() {
+        return mPipActivity;
+    }
+
+    /**
      * Only set flag to the parent tasks and activity itself.
      */
     private void setTransientLaunchToChanges(@NonNull WindowContainer wc) {
@@ -1597,13 +1614,6 @@
             }
         }
 
-        // Take task snapshots before the animation so that we can capture IME before it gets
-        // transferred. If transition is transient, IME won't be moved during the transition and
-        // the tasks are still live, so we take the snapshot at the end of the transition instead.
-        if (mTransientLaunches == null) {
-            mController.mSnapshotController.onTransactionReady(mType, mTargets);
-        }
-
         // This is non-null only if display has changes. It handles the visible windows that don't
         // need to be participated in the transition.
         for (int i = 0; i < mTargetDisplays.size(); ++i) {
@@ -1654,6 +1664,16 @@
 
         reportStartReasonsToLogger();
 
+        // Take snapshots for closing tasks/activities before the animation finished but after
+        // dispatching onTransitionReady, so IME (if there is) can be captured together and the
+        // time spent on snapshot won't delay the start of animation. Note that if this transition
+        // is transient (mTransientLaunches != null), the snapshot will be captured at the end of
+        // the transition, because IME won't move be moved during the transition and the tasks are
+        // still live.
+        if (mTransientLaunches == null) {
+            mController.mSnapshotController.onTransactionReady(mType, mTargets);
+        }
+
         // Since we created root-leash but no longer reference it from core, release it now
         info.releaseAnimSurfaces();
 
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 0d78701..7d2933a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -705,13 +705,21 @@
         try {
             ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                     "Requesting StartTransition: %s", transition);
-            ActivityManager.RunningTaskInfo info = null;
+            ActivityManager.RunningTaskInfo startTaskInfo = null;
+            ActivityManager.RunningTaskInfo pipTaskInfo = null;
             if (startTask != null) {
-                info = new ActivityManager.RunningTaskInfo();
-                startTask.fillTaskInfo(info);
+                startTaskInfo = startTask.getTaskInfo();
             }
-            final TransitionRequestInfo request = new TransitionRequestInfo(
-                    transition.mType, info, remoteTransition, displayChange, transition.getFlags());
+
+            // set the pip task in the request if provided
+            if (mCollectingTransition.getPipActivity() != null) {
+                pipTaskInfo = mCollectingTransition.getPipActivity().getTask().getTaskInfo();
+            }
+
+            final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
+                    startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
+                    transition.getFlags());
+
             transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
             transition.mLogger.mRequest = request;
             mTransitionPlayer.requestStartTransition(transition.getToken(), request);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 805e7ff..a4d43d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -45,6 +45,7 @@
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.inputmethod.ImeTracker;
+import android.window.ScreenCapture;
 
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.server.input.InputManagerService;
@@ -954,4 +955,19 @@
 
     /** Returns the SurfaceControl accessibility services should use for accessibility overlays. */
     public abstract SurfaceControl getA11yOverlayLayer(int displayId);
+
+    /**
+     * Captures the entire display specified by the displayId using the args provided. If the args
+     * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
+     */
+    public abstract void captureDisplay(int displayId,
+                                        @Nullable ScreenCapture.CaptureArgs captureArgs,
+                                        ScreenCapture.ScreenCaptureListener listener);
+
+    /**
+     * Device has a software navigation bar (separate from the status bar) on specific display.
+     *
+     * @param displayId the id of display to check if there is a software navigation bar.
+     */
+    public abstract boolean hasNavigationBar(int displayId);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e720446..8fe104c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -97,6 +97,7 @@
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
 import static android.window.WindowProviderService.isWindowProviderService;
+
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -333,7 +334,6 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
-import com.android.window.flags.FeatureFlagsImpl;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -1175,8 +1175,7 @@
 
         mLetterboxConfiguration = new LetterboxConfiguration(
                 // Using SysUI context to have access to Material colors extracted from Wallpaper.
-                ActivityThread.currentActivityThread().getSystemUiContext(),
-                new FeatureFlagsImpl());
+                ActivityThread.currentActivityThread().getSystemUiContext());
 
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2494,6 +2493,14 @@
                 outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
             }
 
+            // TODO (b/298562855): Remove this after identifying the reason why the frame is empty.
+            if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) {
+                Slog.w(TAG, "Empty frame of " + win
+                        + " configChanged=" + configChanged
+                        + " frame=" + win.getFrame().toShortString()
+                        + " attrs=" + attrs);
+            }
+
             ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
                     win, focusMayChange);
 
@@ -8408,6 +8415,17 @@
         }
 
         @Override
+        public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
+                                   ScreenCapture.ScreenCaptureListener listener) {
+            WindowManagerService.this.captureDisplay(displayId, captureArgs, listener);
+        }
+
+        @Override
+        public boolean hasNavigationBar(int displayId) {
+            return WindowManagerService.this.hasNavigationBar(displayId);
+        }
+
+        @Override
         public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
             synchronized (mGlobalLock) {
                 mImeTargetChangeListener = listener;
@@ -8869,6 +8887,11 @@
             h.inputConfig |= InputConfig.NOT_FOCUSABLE;
         }
 
+        //  Check private trusted overlay flag to set trustedOverlay field of input window handle.
+        if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
+            h.inputConfig |= InputConfig.TRUSTED_OVERLAY;
+        }
+
         h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         h.ownerUid = callingUid;
         h.ownerPid = callingPid;
@@ -8888,8 +8911,6 @@
         }
 
         final SurfaceControl.Transaction t = mTransactionFactory.get();
-        //  Check private trusted overlay flag to set trustedOverlay field of input window handle.
-        h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0);
         t.setInputWindowInfo(surface, h);
         t.apply();
         t.close();
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bfe0553..74a0bafd3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -1279,6 +1279,7 @@
             mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx();
             mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
             mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+            mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier();
             mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled();
             mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
             mLetterboxConfiguration.resetEnabledAutomaticReachabilityInBookMode();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 822082b..b12cc0b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,7 +28,6 @@
 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.InputWindowHandle.USE_SURFACE_TRUSTED_OVERLAY;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.SurfaceControl.getGlobalTransaction;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -99,6 +98,7 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -1112,9 +1112,7 @@
         mInputWindowHandle.setName(getName());
         mInputWindowHandle.setPackageName(mAttrs.packageName);
         mInputWindowHandle.setLayoutParamsType(mAttrs.type);
-        if (!USE_SURFACE_TRUSTED_OVERLAY) {
-            mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay());
-        }
+        mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s));
         if (DEBUG) {
             Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
                             + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -1195,12 +1193,12 @@
                 : service.mAtmService.getProcessController(s.mPid, s.mUid);
     }
 
-    private boolean isWindowTrustedOverlay() {
+    boolean shouldWindowHandleBeTrusted(Session s) {
         return InputMonitor.isTrustedOverlay(mAttrs.type)
                 || ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
-                        && mSession.mCanAddInternalSystemWindow)
+                        && s.mCanAddInternalSystemWindow)
                 || ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
-                        && mSession.mCanCreateSystemApplicationOverlay);
+                        && s.mCanCreateSystemApplicationOverlay);
     }
 
     int getTouchOcclusionMode() {
@@ -5194,9 +5192,6 @@
             updateFrameRateSelectionPriorityIfNeeded();
             updateScaleIfNeeded();
             mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
-            if (USE_SURFACE_TRUSTED_OVERLAY) {
-                getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
-            }
         }
         super.prepareSurfaces();
     }
@@ -5949,13 +5944,7 @@
     }
 
     boolean isTrustedOverlay() {
-        if (USE_SURFACE_TRUSTED_OVERLAY) {
-            WindowState parentWindow = getParentWindow();
-            return isWindowTrustedOverlay() || (parentWindow != null
-                    && parentWindow.isWindowTrustedOverlay());
-        } else {
-            return mInputWindowHandle.isTrustedOverlay();
-        }
+        return mInputWindowHandle.isTrustedOverlay();
     }
 
     public boolean receiveFocusFromTapOutside() {
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 d22e02e..4203e89 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -50,6 +50,13 @@
                             maxOccurs="1"/>
                 <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
                             maxOccurs="1"/>
+
+                <xs:element name="hdrBrightnessConfig" type="hdrBrightnessConfig"
+                            minOccurs="0" maxOccurs="1">
+                    <xs:annotation name="nullable"/>
+                    <xs:annotation name="final"/>
+                </xs:element>
+
                 <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1"/>
                 <xs:element type="autoBrightness" name="autoBrightness" minOccurs="0"
                             maxOccurs="1"/>
@@ -238,6 +245,31 @@
         </xs:all>
     </xs:complexType>
 
+    <!-- brightness config for HDR content -->
+    <xs:complexType name="hdrBrightnessConfig">
+        <!-- lux level from light sensor to screen brightness recommended max value map. -->
+        <xs:element name="brightnessMap" type="nonNegativeFloatToFloatMap">
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
+        <!-- Debounce for brightness increase in millis -->
+        <xs:element name="brightnessIncreaseDebounceMillis" type="xs:nonNegativeInteger">
+            <xs:annotation name="final"/>
+        </xs:element>
+        <!-- Debounce for brightness decrease in millis -->
+        <xs:element name="brightnessDecreaseDebounceMillis" type="xs:nonNegativeInteger">
+            <xs:annotation name="final"/>
+        </xs:element>
+        <!-- Animation time for brightness increase in millis -->
+        <xs:element  name="brightnessIncreaseDurationMillis" type="xs:nonNegativeInteger">
+            <xs:annotation name="final"/>
+        </xs:element>
+        <!-- Animation time for brightness decrease in millis -->
+        <xs:element name="brightnessDecreaseDurationMillis" type="xs:nonNegativeInteger">
+            <xs:annotation name="final"/>
+        </xs:element>
+    </xs:complexType>
+
     <!-- Maps to PowerManager.THERMAL_STATUS_* values. -->
     <xs:simpleType name="thermalStatus">
         <xs:restriction base="xs:string">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 6364c1f..ebd9b1c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -101,6 +101,7 @@
     method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
     method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
     method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
+    method @Nullable public final com.android.server.display.config.HdrBrightnessConfig getHdrBrightnessConfig();
     method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
     method public final com.android.server.display.config.SensorDetails getLightSensor();
     method public com.android.server.display.config.LuxThrottling getLuxThrottling();
@@ -130,6 +131,7 @@
     method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
     method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
     method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
+    method public final void setHdrBrightnessConfig(@Nullable com.android.server.display.config.HdrBrightnessConfig);
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
     method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
@@ -168,6 +170,20 @@
     method public final void setTimeWindowSecs_all(@NonNull java.math.BigInteger);
   }
 
+  public class HdrBrightnessConfig {
+    ctor public HdrBrightnessConfig();
+    method public final java.math.BigInteger getBrightnessDecreaseDebounceMillis();
+    method public final java.math.BigInteger getBrightnessDecreaseDurationMillis();
+    method public final java.math.BigInteger getBrightnessIncreaseDebounceMillis();
+    method public final java.math.BigInteger getBrightnessIncreaseDurationMillis();
+    method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getBrightnessMap();
+    method public final void setBrightnessDecreaseDebounceMillis(java.math.BigInteger);
+    method public final void setBrightnessDecreaseDurationMillis(java.math.BigInteger);
+    method public final void setBrightnessIncreaseDebounceMillis(java.math.BigInteger);
+    method public final void setBrightnessIncreaseDurationMillis(java.math.BigInteger);
+    method public final void setBrightnessMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+  }
+
   public class HighBrightnessMode {
     ctor public HighBrightnessMode();
     method @NonNull public final boolean getAllowInLowPowerMode_all();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index d8c684f..a4adf58 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -35,11 +35,13 @@
 import android.credentials.CreateCredentialRequest;
 import android.credentials.CredentialOption;
 import android.credentials.CredentialProviderInfo;
+import android.credentials.GetCandidateCredentialsException;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialRequest;
 import android.credentials.IClearCredentialStateCallback;
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.ICredentialManager;
+import android.credentials.IGetCandidateCredentialsCallback;
 import android.credentials.IGetCredentialCallback;
 import android.credentials.IPrepareGetCredentialCallback;
 import android.credentials.ISetEnabledProvidersCallback;
@@ -96,6 +98,9 @@
     private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
             "enable_credential_manager";
 
+    private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
+            "enable_credential_description_api";
+
     private final Context mContext;
 
     /** Cache of system service list per user id. */
@@ -298,9 +303,9 @@
             ComponentName compName = ComponentName.unflattenFromString(serviceName);
             if (compName == null) {
                 Slog.w(
-                    TAG,
-                    "Primary provider component name unflatten from string error: "
-                            + serviceName);
+                        TAG,
+                        "Primary provider component name unflatten from string error: "
+                                + serviceName);
                 continue;
             }
             services.add(compName);
@@ -321,7 +326,14 @@
     }
 
     public static boolean isCredentialDescriptionApiEnabled() {
-        return true;
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
+                    false);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
     }
 
     @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
@@ -461,6 +473,59 @@
 
     final class CredentialManagerServiceStub extends ICredentialManager.Stub {
         @Override
+        public ICancellationSignal getCandidateCredentials(
+                GetCredentialRequest request,
+                IGetCandidateCredentialsCallback callback,
+                final String callingPackage) {
+            Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
+                    + callingPackage);
+            ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+            final int userId = UserHandle.getCallingUserId();
+            final int callingUid = Binder.getCallingUid();
+
+            // New request session, scoped for this request only.
+            final GetCandidateRequestSession session =
+                    new GetCandidateRequestSession(
+                            getContext(),
+                            mSessionManager,
+                            mLock,
+                            userId,
+                            callingUid,
+                            callback,
+                            request,
+                            constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
+                            getEnabledProvidersForUser(userId),
+                            CancellationSignal.fromTransport(cancelTransport)
+                    );
+            addSessionLocked(userId, session);
+
+            List<ProviderSession> providerSessions =
+                    initiateProviderSessions(
+                            session,
+                            request.getCredentialOptions().stream()
+                                    .map(CredentialOption::getType)
+                                    .collect(Collectors.toList()));
+
+            if (providerSessions.isEmpty()) {
+                try {
+                    callback.onError(
+                            GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+                            "No credentials available on this device.");
+                } catch (RemoteException e) {
+                    Slog.i(
+                            TAG,
+                            "Issue invoking onError on IGetCredentialCallback "
+                                    + "callback: "
+                                    + e.getMessage());
+                }
+            }
+
+            invokeProviderSessions(providerSessions);
+            return cancelTransport;
+        }
+
+        @Override
         public ICancellationSignal executeGetCredential(
                 GetCredentialRequest request,
                 IGetCredentialCallback callback,
@@ -714,7 +779,7 @@
 
         @Override
         public void setEnabledProviders(
-                List<String>  primaryProviders, List<String> providers, int userId,
+                List<String> primaryProviders, List<String> providers, int userId,
                 ISetEnabledProvidersCallback callback) {
             final int callingUid = Binder.getCallingUid();
             if (!hasWriteSecureSettingsPermission()) {
@@ -839,9 +904,9 @@
                     ApiName.GET_CREDENTIAL_PROVIDER_SERVICES,
                     ApiStatus.SUCCESS, callingUid);
             return CredentialProviderInfoFactory
-            .getCredentialProviderServices(
-                mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
-                getPrimaryProvidersForUserId(mContext, userId));
+                    .getCredentialProviderServices(
+                            mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
+                            getPrimaryProvidersForUserId(mContext, userId));
 
         }
 
@@ -871,13 +936,14 @@
 
         private Set<ComponentName> getEnabledProvidersForUser(int userId) {
             final int resolvedUserId = ActivityManager.handleIncomingUser(
-                Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, false,
-                "getEnabledProvidersForUser", null);
+                    Binder.getCallingPid(), Binder.getCallingUid(),
+                    userId, false, false,
+                    "getEnabledProvidersForUser", null);
 
             Set<ComponentName> enabledProviders = new HashSet<>();
             String directValue = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE, resolvedUserId);
+                    mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE,
+                    resolvedUserId);
 
             if (!TextUtils.isEmpty(directValue)) {
                 String[] components = directValue.split(":");
@@ -954,7 +1020,7 @@
             Slog.i(TAG, "registerCredentialDescription with callingPackage: " + callingPackage);
 
             if (!isCredentialDescriptionApiEnabled()) {
-                throw new UnsupportedOperationException();
+                throw new UnsupportedOperationException("Feature not supported");
             }
 
             enforceCallingPackage(callingPackage, Binder.getCallingUid());
@@ -974,7 +1040,7 @@
 
 
             if (!isCredentialDescriptionApiEnabled()) {
-                throw new UnsupportedOperationException();
+                throw new UnsupportedOperationException("Feature not supported");
             }
 
             enforceCallingPackage(callingPackage, Binder.getCallingUid());
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
new file mode 100644
index 0000000..6d9b7e8
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2022 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.credentials;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.credentials.CredentialProviderInfo;
+import android.credentials.GetCandidateCredentialsException;
+import android.credentials.GetCandidateCredentialsResponse;
+import android.credentials.GetCredentialRequest;
+import android.credentials.IGetCandidateCredentialsCallback;
+import android.credentials.ui.GetCredentialProviderData;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.RequestInfo;
+import android.os.CancellationSignal;
+import android.os.RemoteException;
+import android.service.credentials.CallingAppInfo;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Central session for a single getCandidateCredentials request. This class listens to the
+ * responses from providers, and updates the provider(s) state.
+ */
+public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
+        IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
+        implements ProviderSession.ProviderInternalCallback<GetCandidateCredentialsResponse> {
+    private static final String TAG = "GetCandidateRequestSession";
+
+    public GetCandidateRequestSession(
+            Context context, SessionLifetime sessionCallback,
+            Object lock, int userId, int callingUid,
+            IGetCandidateCredentialsCallback callback, GetCredentialRequest request,
+            CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders,
+            CancellationSignal cancellationSignal) {
+        super(context, sessionCallback, lock, userId, callingUid, request, callback,
+                RequestInfo.TYPE_GET, callingAppInfo, enabledProviders,
+                cancellationSignal, 0L);
+    }
+
+    /**
+     * Creates a new provider session, and adds it list of providers that are contributing to
+     * this session.
+     *
+     * @return the provider session created within this request session, for the given provider
+     * info.
+     */
+    @Override
+    @Nullable
+    public ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
+            RemoteCredentialService remoteCredentialService) {
+        ProviderGetSession providerGetCandidateSessions = ProviderGetSession
+                .createNewSession(mContext, mUserId, providerInfo,
+                        this, remoteCredentialService);
+        if (providerGetCandidateSessions != null) {
+            Slog.d(TAG, "In startProviderSession - provider session created and "
+                    + "being added for: " + providerInfo.getComponentName());
+            mProviders.put(providerGetCandidateSessions.getComponentName().flattenToString(),
+                    providerGetCandidateSessions);
+        }
+        return providerGetCandidateSessions;
+    }
+
+    /**
+     * Even though there is no UI involved, this is called when all providers are ready
+     * in our current flow. Eventually can completely separate UI and non UI flows.
+     */
+    @Override
+    protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
+        if (providerDataList == null || providerDataList.isEmpty()) {
+            respondToClientWithErrorAndFinish(
+                    GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+                    "No credentials found");
+            return;
+        }
+
+        List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
+        for (ProviderData providerData : providerDataList) {
+            candidateProviderDataList.add((GetCredentialProviderData) (providerData));
+        }
+        respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(
+                candidateProviderDataList));
+    }
+
+    @Override
+    protected void invokeClientCallbackSuccess(GetCandidateCredentialsResponse response)
+            throws RemoteException {
+        mClientCallback.onResponse(response);
+    }
+
+    @Override
+    protected void invokeClientCallbackError(String errorType, String errorMsg)
+            throws RemoteException {
+        mClientCallback.onError(errorType, errorMsg);
+    }
+
+    @Override
+    public void onFinalErrorReceived(ComponentName componentName, String errorType,
+            String message) {
+        // Not applicable for session without UI
+    }
+
+    @Override
+    public void onUiCancellation(boolean isUserCancellation) {
+        // Not applicable for session without UI
+    }
+
+    @Override
+    public void onUiSelectorInvocationFailure() {
+        // Not applicable for session without UI
+    }
+
+    @Override
+    public void onProviderStatusChanged(ProviderSession.Status status,
+            ComponentName componentName, ProviderSession.CredentialsSource source) {
+        Slog.d(TAG, "in onStatusChanged with status: " + status + ", and source: " + source);
+
+        // For any other status, we check if all providers are done and then invoke UI if needed
+        if (!isAnyProviderPending()) {
+            // If all provider responses have been received, we can either need the UI,
+            // or we need to respond with error. The only other case is the entry being
+            // selected after the UI has been invoked which has a separate code path.
+            if (isUiInvocationNeeded()) {
+                Slog.d(TAG, "in onProviderStatusChanged - isUiInvocationNeeded");
+                getProviderDataAndInitiateUi();
+            } else {
+                respondToClientWithErrorAndFinish(
+                        GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
+                        "No credentials available");
+            }
+        }
+    }
+
+    @Override
+    public void onFinalResponseReceived(ComponentName componentName,
+            GetCandidateCredentialsResponse response) {
+        // Not applicable for session without UI
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 3c1432a..3eb6718 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -119,6 +119,42 @@
         return null;
     }
 
+    /** Creates a new provider session to be used by the request session. */
+    @Nullable
+    public static ProviderGetSession createNewSession(
+            Context context,
+            @UserIdInt int userId,
+            CredentialProviderInfo providerInfo,
+            GetCandidateRequestSession getRequestSession,
+            RemoteCredentialService remoteCredentialService) {
+        android.credentials.GetCredentialRequest filteredRequest =
+                filterOptions(providerInfo.getCapabilities(),
+                        getRequestSession.mClientRequest,
+                        providerInfo);
+        if (filteredRequest != null) {
+            Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
+                    new HashMap<>();
+            return new ProviderGetSession(
+                    context,
+                    providerInfo,
+                    getRequestSession,
+                    userId,
+                    remoteCredentialService,
+                    constructQueryPhaseRequest(
+                            filteredRequest, getRequestSession.mClientAppInfo,
+                            getRequestSession.mClientRequest.alwaysSendAppInfoToProvider(),
+                            beginGetOptionToCredentialOptionMap),
+                    filteredRequest,
+                    getRequestSession.mClientAppInfo,
+                    beginGetOptionToCredentialOptionMap,
+                    getRequestSession.mHybridService
+            );
+        }
+        Slog.i(TAG, "Unable to create provider session for: "
+                + providerInfo.getComponentName());
+        return null;
+    }
+
     private static BeginGetCredentialRequest constructQueryPhaseRequest(
             android.credentials.GetCredentialRequest filteredRequest,
             CallingAppInfo callingAppInfo,
@@ -192,7 +228,7 @@
 
     public ProviderGetSession(Context context,
             CredentialProviderInfo info,
-            ProviderInternalCallback<GetCredentialResponse> callbacks,
+            ProviderInternalCallback callbacks,
             int userId, RemoteCredentialService remoteCredentialService,
             BeginGetCredentialRequest beginGetRequest,
             android.credentials.GetCredentialRequest completeGetRequest,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 21b1291..d0ead14 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1472,11 +1472,10 @@
         synchronized (mLock) {
             clear();
             new DevicePoliciesReaderWriter().readFromFileLocked();
-            reapplyAllPoliciesLocked();
         }
     }
 
-    private <V> void reapplyAllPoliciesLocked() {
+    <V> void reapplyAllPoliciesLocked() {
         for (PolicyKey policy : mGlobalPolicies.keySet()) {
             PolicyState<?> policyState = mGlobalPolicies.get(policy);
             // Policy definition and value will always be of the same type
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 50dc061..84d1a45 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3347,6 +3347,11 @@
                 mOwners.systemReady();
                 applyManagedSubscriptionsPolicyIfRequired();
                 break;
+            case SystemService.PHASE_SYSTEM_SERVICES_READY:
+                synchronized (getLockObject()) {
+                    mDevicePolicyEngine.reapplyAllPoliciesLocked();
+                }
+                break;
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
                 synchronized (getLockObject()) {
                     migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
@@ -15836,6 +15841,83 @@
     }
 
     /**
+     * @param restriction The restriction enforced by admin. It could be any user restriction or
+     *                    policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA},
+     *                    {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE} and  {@link
+     *                    DevicePolicyManager#POLICY_SUSPEND_PACKAGES}.
+     */
+    private Set<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestrictionInternal(
+            int userId, @NonNull String restriction) {
+        Objects.requireNonNull(restriction);
+        Set<android.app.admin.EnforcingAdmin> admins = new HashSet<>();
+        // For POLICY_SUSPEND_PACKAGES return PO or DO to keep the behavior same as
+        // before the bug fix for b/192245204.
+        if (DevicePolicyManager.POLICY_SUSPEND_PACKAGES.equals(
+                restriction)) {
+            ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+            if (profileOwner != null) {
+                EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        profileOwner, userId);
+                admins.add(admin.getParcelableAdmin());
+                return admins;
+            }
+            final Pair<Integer, ComponentName> deviceOwner =
+                    mOwners.getDeviceOwnerUserIdAndComponent();
+            if (deviceOwner != null && deviceOwner.first == userId) {
+                EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        deviceOwner.second, deviceOwner.first);
+                admins.add(admin.getParcelableAdmin());
+                return admins;
+            }
+        } else {
+            long ident = mInjector.binderClearCallingIdentity();
+            try {
+                PolicyDefinition<Boolean> policyDefinition = getPolicyDefinitionForRestriction(
+                        restriction);
+                Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+                if (value != null && value) {
+                    Map<EnforcingAdmin, PolicyValue<Boolean>> globalPolicies =
+                            mDevicePolicyEngine.getGlobalPoliciesSetByAdmins(policyDefinition);
+                    for (EnforcingAdmin admin : globalPolicies.keySet()) {
+                        if (globalPolicies.get(admin) != null
+                                && Boolean.TRUE.equals(globalPolicies.get(admin).getValue())) {
+                            admins.add(admin.getParcelableAdmin());
+                        }
+                    }
+
+                    Map<EnforcingAdmin, PolicyValue<Boolean>> localPolicies =
+                            mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+                                    policyDefinition, userId);
+                    for (EnforcingAdmin admin : localPolicies.keySet()) {
+                        if (localPolicies.get(admin) != null
+                                && Boolean.TRUE.equals(localPolicies.get(admin).getValue())) {
+                            admins.add(admin.getParcelableAdmin());
+                        }
+                    }
+                    return admins;
+                }
+            } finally {
+                mInjector.binderRestoreCallingIdentity(ident);
+            }
+        }
+        return admins;
+    }
+
+    private static PolicyDefinition<Boolean> getPolicyDefinitionForRestriction(
+            @NonNull String restriction) {
+        Objects.requireNonNull(restriction);
+        if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
+            return PolicyDefinition.getPolicyDefinitionForUserRestriction(
+                    UserManager.DISALLOW_CAMERA);
+        } else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+            return PolicyDefinition.SCREEN_CAPTURE_DISABLED;
+        } else {
+            return PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction);
+        }
+    }
+
+
+    /**
      *  Excludes restrictions imposed by UserManager.
      */
     private List<UserManager.EnforcingUser> getDevicePolicySources(
@@ -15873,6 +15955,13 @@
         return getEnforcingAdminAndUserDetailsInternal(userId, restriction);
     }
 
+    @Override
+    public List<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestriction(
+            int userId, String restriction) {
+        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+        return new ArrayList<>(getEnforcingAdminsForRestrictionInternal(userId, restriction));
+    }
+
     /**
      * @param restriction The restriction enforced by admin. It could be any user restriction or
      *                    policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index 5243d14..0066422 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -228,7 +228,8 @@
         return new android.app.admin.EnforcingAdmin(
                 mPackageName,
                 authority,
-                UserHandle.of(mUserId));
+                UserHandle.of(mUserId),
+                mComponentName);
     }
 
     /**
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 8279600..cee2524 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 = checkNotNull(AppOpsManager.permissionToOp(permissionName))
+        val appOpName = AppOpsManager.permissionToOp(permissionName)!!
         val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
         with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
     }
@@ -1753,10 +1753,19 @@
     }
 
     override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
+        context.enforceCallingOrSelfPermission(
+            Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"
+        )
+
         onPermissionsChangeListeners.addListener(listener)
     }
 
     override fun removeOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
+        context.enforceCallingOrSelfPermission(
+            Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
+            "removeOnPermissionsChangeListener"
+        )
+
         onPermissionsChangeListeners.removeListener(listener)
     }
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index dc2fbfc..2889c74 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -24,9 +24,7 @@
 import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
 import static android.content.pm.parsing.FrameworkParsingPackageUtils.parsePublicKey;
 import static android.content.res.Resources.ID_NULL;
-
 import static com.android.server.pm.PackageManagerService.WRITE_USER_PACKAGE_RESTRICTIONS;
-
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
@@ -997,7 +995,7 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/,
                 UUID.randomUUID());
-        origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false,
+        origPkgSetting01.setUserState(0, 100, 100, 1, true, false, false, false, 0, null, false,
                 false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
                 new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
                 "splashScreenTheme", 1000L, PackageManager.USER_MIN_ASPECT_RATIO_UNSET, null);
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
index 3ef3a89..3cf57a3 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.server.permission.test
 
+import android.Manifest
 import android.content.pm.PackageManager
 import android.content.pm.PermissionGroupInfo
 import android.content.pm.PermissionInfo
+import android.content.pm.SigningDetails
+import android.os.Build
 import android.os.Bundle
 import android.util.ArrayMap
-import android.util.ArraySet
 import android.util.SparseArray
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.modules.utils.testing.ExtendedMockitoRule
@@ -33,12 +35,15 @@
 import com.android.server.permission.access.permission.AppIdPermissionPolicy
 import com.android.server.permission.access.permission.Permission
 import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.permission.access.util.hasBits
 import com.android.server.pm.parsing.PackageInfoUtils
+import com.android.server.pm.permission.PermissionAllowlist
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageState
 import com.android.server.pm.pkg.PackageUserState
 import com.android.server.pm.pkg.component.ParsedPermission
 import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.server.testutils.any
 import com.android.server.testutils.mock
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertWithMessage
@@ -46,6 +51,7 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyLong
 
 /**
  * Mocking unit test for AppIdPermissionPolicy.
@@ -55,11 +61,16 @@
     private lateinit var oldState: MutableAccessState
     private lateinit var newState: MutableAccessState
 
-    private lateinit var androidPackage0: AndroidPackage
-    private lateinit var androidPackage1: AndroidPackage
-
-    private lateinit var packageState0: PackageState
-    private lateinit var packageState1: PackageState
+    private val defaultPermissionGroup = mockParsedPermissionGroup(
+        PERMISSION_GROUP_NAME_0,
+        PACKAGE_NAME_0
+    )
+    private val defaultPermissionTree = mockParsedPermission(
+        PERMISSION_TREE_NAME,
+        PACKAGE_NAME_0,
+        isTree = true
+    )
+    private val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
 
     private val appIdPermissionPolicy = AppIdPermissionPolicy()
 
@@ -70,160 +81,124 @@
         .build()
 
     @Before
-    fun init() {
+    fun setUp() {
         oldState = MutableAccessState()
         createUserState(USER_ID_0)
-        createUserState(USER_ID_1)
         oldState.mutateExternalState().setPackageStates(ArrayMap())
-
-        androidPackage0 = mockAndroidPackage(
-            PACKAGE_NAME_0,
-            PERMISSION_GROUP_NAME_0,
-            PERMISSION_NAME_0
-        )
-        androidPackage1 = mockAndroidPackage(
-            PACKAGE_NAME_1,
-            PERMISSION_GROUP_NAME_1,
-            PERMISSION_NAME_1
-        )
-
-        packageState0 = mockPackageState(APP_ID_0, androidPackage0)
-        packageState1 = mockPackageState(APP_ID_1, androidPackage1)
-    }
-
-    private fun mockAndroidPackage(
-        packageName: String,
-        permissionGroupName: String,
-        permissionName: String,
-    ): AndroidPackage {
-        val parsedPermissionGroup = mock<ParsedPermissionGroup> {
-            whenever(name).thenReturn(permissionGroupName)
-            whenever(metaData).thenReturn(Bundle())
-        }
-
-        @Suppress("DEPRECATION")
-        val permissionGroupInfo = PermissionGroupInfo().apply {
-            name = permissionGroupName
-            this.packageName = packageName
-        }
-        wheneverStatic {
-            PackageInfoUtils.generatePermissionGroupInfo(
-                parsedPermissionGroup,
-                PackageManager.GET_META_DATA.toLong()
-            )
-        }.thenReturn(permissionGroupInfo)
-
-        val parsedPermission = mock<ParsedPermission> {
-            whenever(name).thenReturn(permissionName)
-            whenever(isTree).thenReturn(false)
-            whenever(metaData).thenReturn(Bundle())
-        }
-
-        @Suppress("DEPRECATION")
-        val permissionInfo = PermissionInfo().apply {
-            name = permissionName
-            this.packageName = packageName
-        }
-        wheneverStatic {
-            PackageInfoUtils.generatePermissionInfo(
-                parsedPermission,
-                PackageManager.GET_META_DATA.toLong()
-            )
-        }.thenReturn(permissionInfo)
-
-        val requestedPermissions = ArraySet<String>()
-        return mock {
-            whenever(this.packageName).thenReturn(packageName)
-            whenever(this.requestedPermissions).thenReturn(requestedPermissions)
-            whenever(permissionGroups).thenReturn(listOf(parsedPermissionGroup))
-            whenever(permissions).thenReturn(listOf(parsedPermission))
-            whenever(signingDetails).thenReturn(mock {})
-        }
-    }
-
-    private fun mockPackageState(
-        appId: Int,
-        androidPackage: AndroidPackage,
-    ): PackageState {
-        val packageName = androidPackage.packageName
-        oldState.mutateExternalState().mutateAppIdPackageNames().mutateOrPut(appId) {
-            MutableIndexedListSet()
-        }.add(packageName)
-
-        val userStates = SparseArray<PackageUserState>().apply {
-            put(USER_ID_0, mock { whenever(isInstantApp).thenReturn(false) })
-        }
-        val mockPackageState: PackageState = mock {
-            whenever(this.packageName).thenReturn(packageName)
-            whenever(this.appId).thenReturn(appId)
-            whenever(this.androidPackage).thenReturn(androidPackage)
-            whenever(isSystem).thenReturn(false)
-            whenever(this.userStates).thenReturn(userStates)
-        }
-        oldState.mutateExternalState().setPackageStates(
-            oldState.mutateExternalState().packageStates.toMutableMap().apply {
-                put(packageName, mockPackageState)
-            }
-        )
-        return mockPackageState
+        oldState.mutateExternalState().setDisabledSystemPackageStates(ArrayMap())
+        mockPackageInfoUtilsGeneratePermissionInfo()
+        mockPackageInfoUtilsGeneratePermissionGroupInfo()
     }
 
     private fun createUserState(userId: Int) {
+        oldState.mutateExternalState().mutateUserIds().add(userId)
         oldState.mutateUserStatesNoWrite().put(userId, MutableUserState())
     }
 
+    private fun mockPackageInfoUtilsGeneratePermissionInfo() {
+        wheneverStatic {
+            PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong())
+        }.thenAnswer { invocation ->
+            val parsedPermission = invocation.getArgument<ParsedPermission>(0)
+            val generateFlags = invocation.getArgument<Long>(1)
+            PermissionInfo(parsedPermission.backgroundPermission).apply {
+                name = parsedPermission.name
+                packageName = parsedPermission.packageName
+                metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
+                    parsedPermission.metaData
+                } else {
+                    null
+                }
+                @Suppress("DEPRECATION")
+                protectionLevel = parsedPermission.protectionLevel
+                group = parsedPermission.group
+                flags = parsedPermission.flags
+            }
+        }
+    }
+
+    private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() {
+        wheneverStatic {
+            PackageInfoUtils.generatePermissionGroupInfo(
+                any(ParsedPermissionGroup::class.java),
+                anyLong()
+            )
+        }.thenAnswer { invocation ->
+            val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0)
+            val generateFlags = invocation.getArgument<Long>(1)
+            @Suppress("DEPRECATION")
+            PermissionGroupInfo().apply {
+                name = parsedPermissionGroup.name
+                packageName = parsedPermissionGroup.packageName
+                metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
+                    parsedPermissionGroup.metaData
+                } else {
+                    null
+                }
+                flags = parsedPermissionGroup.flags
+            }
+        }
+    }
+
     @Test
     fun testResetRuntimePermissions_runtimeGranted_getsRevoked() {
         val oldFlags = PermissionFlags.RUNTIME_GRANTED
         val expectedNewFlags = 0
-        testResetRuntimePermissions(oldFlags, expectedNewFlags) {}
+        testResetRuntimePermissions(oldFlags, expectedNewFlags)
     }
 
     @Test
     fun testResetRuntimePermissions_roleGranted_getsGranted() {
         val oldFlags = PermissionFlags.ROLE
         val expectedNewFlags = PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED
-        testResetRuntimePermissions(oldFlags, expectedNewFlags) {}
+        testResetRuntimePermissions(oldFlags, expectedNewFlags)
     }
 
     @Test
     fun testResetRuntimePermissions_nullAndroidPackage_remainsUnchanged() {
         val oldFlags = PermissionFlags.RUNTIME_GRANTED
         val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
-        testResetRuntimePermissions(oldFlags, expectedNewFlags) {
-            whenever(packageState0.androidPackage).thenReturn(null)
-        }
+        testResetRuntimePermissions(oldFlags, expectedNewFlags, isAndroidPackageMissing = true)
     }
 
-    private inline fun testResetRuntimePermissions(
+    private fun testResetRuntimePermissions(
         oldFlags: Int,
         expectedNewFlags: Int,
-        additionalSetup: () -> Unit
+        isAndroidPackageMissing: Boolean = false
     ) {
-        createSystemStatePermission(
-            APP_ID_0,
-            PACKAGE_NAME_0,
+        val parsedPermission = mockParsedPermission(
             PERMISSION_NAME_0,
-            PermissionInfo.PROTECTION_DANGEROUS
+            PACKAGE_NAME_0,
+            protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
         )
-        androidPackage0.requestedPermissions.add(PERMISSION_NAME_0)
-        oldState.mutateUserState(USER_ID_0)!!.mutateAppIdPermissionFlags().mutateOrPut(APP_ID_0) {
-            MutableIndexedMap()
-        }.put(PERMISSION_NAME_0, oldFlags)
-
-        additionalSetup()
+        val permissionOwnerPackageState = mockPackageState(
+            APP_ID_0,
+            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+        )
+        val requestingPackageState = if (isAndroidPackageMissing) {
+            mockPackageState(APP_ID_1, PACKAGE_NAME_1)
+        } else {
+            mockPackageState(
+                APP_ID_1,
+                mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+            )
+        }
+        setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+        addPackageState(permissionOwnerPackageState)
+        addPackageState(requestingPackageState)
+        addPermission(parsedPermission)
 
         mutateState {
             with(appIdPermissionPolicy) {
-                resetRuntimePermissions(PACKAGE_NAME_0, USER_ID_0)
+                resetRuntimePermissions(PACKAGE_NAME_1, USER_ID_0)
             }
         }
 
-        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
         assertWithMessage(
             "After resetting runtime permissions, permission flags did not match" +
-            " expected values: expectedNewFlags is $expectedNewFlags," +
-            " actualFlags is $actualFlags, while the oldFlags is $oldFlags"
+                " expected values: expectedNewFlags is $expectedNewFlags," +
+                " actualFlags is $actualFlags, while the oldFlags is $oldFlags"
         )
             .that(actualFlags)
             .isEqualTo(expectedNewFlags)
@@ -231,285 +206,1685 @@
 
     @Test
     fun testOnPackageAdded_permissionsOfMissingSystemApp_getsAdopted() {
-        testOnPackageAdded {
-            adoptPermissionTestSetup()
-            whenever(packageState1.androidPackage).thenReturn(null)
-        }
+        testAdoptPermissions(hasMissingPackage = true, isSystem = true)
 
-        val permission0 = newState.systemState.permissions[PERMISSION_NAME_0]
         assertWithMessage(
             "After onPackageAdded() is called for a null adopt permission package," +
-            " the permission package name: ${permission0!!.packageName} did not match" +
-            " the expected package name: $PACKAGE_NAME_0"
+                " the permission package name: ${getPermission(PERMISSION_NAME_0)?.packageName}" +
+                " did not match the expected package name: $PACKAGE_NAME_0"
         )
-            .that(permission0.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_permissionsOfExistingSystemApp_notAdopted() {
-        testOnPackageAdded {
-            adoptPermissionTestSetup()
-        }
+        testAdoptPermissions(isSystem = true)
 
-        val permission0 = newState.systemState.permissions[PERMISSION_NAME_0]
         assertWithMessage(
             "After onPackageAdded() is called for a non-null adopt permission" +
-            " package, the permission package name: ${permission0!!.packageName} should" +
-            " not match the package name: $PACKAGE_NAME_0"
+                " package, the permission package name:" +
+                " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" +
+                " package name: $PACKAGE_NAME_0"
         )
-            .that(permission0.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isNotEqualTo(PACKAGE_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_permissionsOfNonSystemApp_notAdopted() {
-        testOnPackageAdded {
-            adoptPermissionTestSetup()
-            whenever(packageState1.isSystem).thenReturn(false)
-        }
+        testAdoptPermissions(hasMissingPackage = true)
 
-        val permission0 = newState.systemState.permissions[PERMISSION_NAME_0]
         assertWithMessage(
             "After onPackageAdded() is called for a non-system adopt permission" +
-                " package, the permission package name: ${permission0!!.packageName} should" +
-                " not match the package name: $PACKAGE_NAME_0"
+                " package, the permission package name:" +
+                " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" +
+                " package name: $PACKAGE_NAME_0"
         )
-            .that(permission0.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isNotEqualTo(PACKAGE_NAME_0)
     }
 
-    private fun adoptPermissionTestSetup() {
-        createSystemStatePermission(
-            APP_ID_1,
-            PACKAGE_NAME_1,
-            PERMISSION_NAME_0,
-            PermissionInfo.PROTECTION_SIGNATURE
-        )
-        whenever(androidPackage0.adoptPermissions).thenReturn(listOf(PACKAGE_NAME_1))
-        whenever(packageState1.isSystem).thenReturn(true)
+    private fun testAdoptPermissions(
+        hasMissingPackage: Boolean = false,
+        isSystem: Boolean = false
+    ) {
+        val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1)
+        val packageToAdoptPermission = if (hasMissingPackage) {
+            mockPackageState(APP_ID_1, PACKAGE_NAME_1, isSystem = isSystem)
+        } else {
+            mockPackageState(
+                APP_ID_1,
+                mockAndroidPackage(
+                    PACKAGE_NAME_1,
+                    permissions = listOf(parsedPermission)
+                ),
+                isSystem = isSystem
+            )
+        }
+        addPackageState(packageToAdoptPermission)
+        addPermission(parsedPermission)
+
+        mutateState {
+            val installedPackage = mockPackageState(
+                APP_ID_0,
+                mockAndroidPackage(
+                    PACKAGE_NAME_0,
+                    permissions = listOf(defaultPermission),
+                    adoptPermissions = listOf(PACKAGE_NAME_1)
+                )
+            )
+            addPackageState(installedPackage, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(installedPackage)
+            }
+        }
     }
 
     @Test
     fun testOnPackageAdded_newPermissionGroup_getsDeclared() {
-        testOnPackageAdded {}
+        mutateState {
+            val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+            addPackageState(packageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(packageState)
+            }
+        }
 
         assertWithMessage(
             "After onPackageAdded() is called when there is no existing" +
-            " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added"
+                " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added"
         )
-            .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.name)
+            .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.name)
             .isEqualTo(PERMISSION_GROUP_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_systemAppTakingOverPermissionGroupDefinition_getsTakenOver() {
-        testOnPackageAdded {
-            whenever(packageState0.isSystem).thenReturn(true)
-            createSystemStatePermissionGroup(PACKAGE_NAME_1, PERMISSION_GROUP_NAME_0)
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true)
 
         assertWithMessage(
             "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" +
-            " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the ownership" +
-            " of this permission group"
+                " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the" +
+                " ownership of this permission group"
         )
-            .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.packageName)
+            .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_instantApps_remainsUnchanged() {
-        testOnPackageAdded {
-            (packageState0.userStates as SparseArray<PackageUserState>).apply {
-                put(0, mock { whenever(isInstantApp).thenReturn(true) })
-            }
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions(
+            newPermissionOwnerIsInstant = true,
+            permissionGroupAlreadyExists = false
+        )
 
         assertWithMessage(
             "After onPackageAdded() is called for an instant app," +
-            " the new permission group $PERMISSION_GROUP_NAME_0 should not be added"
+                " the new permission group $PERMISSION_GROUP_NAME_0 should not be added"
         )
-            .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0])
+            .that(getPermissionGroup(PERMISSION_GROUP_NAME_0))
             .isNull()
     }
 
     @Test
     fun testOnPackageAdded_nonSystemAppTakingOverPermissionGroupDefinition_remainsUnchanged() {
-        testOnPackageAdded {
-            createSystemStatePermissionGroup(PACKAGE_NAME_1, PERMISSION_GROUP_NAME_0)
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions()
 
         assertWithMessage(
             "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" +
-            " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover ownership" +
-            " of this permission group"
+                " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover" +
+                " ownership of this permission group"
         )
-            .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.packageName)
+            .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_1)
     }
 
     @Test
     fun testOnPackageAdded_takingOverPermissionGroupDeclaredBySystemApp_remainsUnchanged() {
-        testOnPackageAdded {
-            whenever(packageState1.isSystem).thenReturn(true)
-            createSystemStatePermissionGroup(PACKAGE_NAME_1, PERMISSION_GROUP_NAME_0)
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true)
 
         assertWithMessage(
             "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" +
-            " exists in the system and is owned by a system app, app $PACKAGE_NAME_0 shouldn't" +
-            " takeover ownership of this permission group"
+                " exists in the system and is owned by a system app, app $PACKAGE_NAME_0" +
+                " shouldn't takeover ownership of this permission group"
         )
-            .that(newState.systemState.permissionGroups[PERMISSION_GROUP_NAME_0]?.packageName)
+            .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_1)
     }
 
     @Test
     fun testOnPackageAdded_newPermission_getsDeclared() {
-        testOnPackageAdded {}
+        mutateState {
+            val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+            addPackageState(packageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(packageState)
+            }
+        }
 
         assertWithMessage(
             "After onPackageAdded() is called when there is no existing" +
-            " permissions, the new permission $PERMISSION_NAME_0 is not added"
+                " permissions, the new permission $PERMISSION_NAME_0 is not added"
         )
-            .that(newState.systemState.permissions[PERMISSION_NAME_0]?.name)
+            .that(getPermission(PERMISSION_NAME_0)?.name)
             .isEqualTo(PERMISSION_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_configPermission_getsTakenOver() {
-        testOnPackageAdded {
-            whenever(packageState0.isSystem).thenReturn(true)
-            createSystemStatePermission(
-                APP_ID_0,
-                PACKAGE_NAME_1,
-                PERMISSION_NAME_0,
-                PermissionInfo.PROTECTION_DANGEROUS,
-                Permission.TYPE_CONFIG,
-                false
-            )
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions(
+            oldPermissionOwnerIsSystem = true,
+            newPermissionOwnerIsSystem = true,
+            type = Permission.TYPE_CONFIG,
+            isReconciled = false
+        )
 
         assertWithMessage(
             "After onPackageAdded() is called for a config permission with" +
-            " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0"
+                " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0"
         )
-            .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_systemAppTakingOverPermissionDefinition_getsTakenOver() {
-        testOnPackageAdded {
-            whenever(packageState0.isSystem).thenReturn(true)
-            createSystemStatePermission(
-                APP_ID_1,
-                PACKAGE_NAME_1,
-                PERMISSION_NAME_0,
-                PermissionInfo.PROTECTION_DANGEROUS
-            )
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true)
 
         assertWithMessage(
             "After onPackageAdded() is called when $PERMISSION_NAME_0 already" +
-            " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the ownership" +
-            " of this permission"
+                " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover ownership" +
+                " of this permission"
         )
-            .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_0)
     }
 
     @Test
     fun testOnPackageAdded_nonSystemAppTakingOverPermissionDefinition_remainsUnchanged() {
-        testOnPackageAdded {
-            createSystemStatePermission(
-                APP_ID_1,
-                PACKAGE_NAME_1,
-                PERMISSION_NAME_0,
-                PermissionInfo.PROTECTION_DANGEROUS
-            )
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions()
 
         assertWithMessage(
             "After onPackageAdded() is called when $PERMISSION_NAME_0 already" +
-            " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" +
-            " ownership of this permission"
+                " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" +
+                " ownership of this permission"
         )
-            .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_1)
     }
 
     @Test
     fun testOnPackageAdded_takingOverPermissionDeclaredBySystemApp_remainsUnchanged() {
-        testOnPackageAdded {
-            whenever(packageState1.isSystem).thenReturn(true)
-            createSystemStatePermission(
-                APP_ID_1,
-                PACKAGE_NAME_1,
-                PERMISSION_NAME_0,
-                PermissionInfo.PROTECTION_DANGEROUS
-            )
-        }
+        testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true)
 
         assertWithMessage(
             "After onPackageAdded() is called when $PERMISSION_NAME_0 already" +
-            " exists in system and is owned by a system app, the app $PACKAGE_NAME_0 shouldn't" +
-            " takeover ownership of this permission"
+                " exists in system and is owned by a system app, the $PACKAGE_NAME_0 shouldn't" +
+                " takeover ownership of this permission"
         )
-            .that(newState.systemState.permissions[PERMISSION_NAME_0]?.packageName)
+            .that(getPermission(PERMISSION_NAME_0)?.packageName)
             .isEqualTo(PACKAGE_NAME_1)
     }
 
-    private inline fun testOnPackageAdded(mockBehaviorOverride: () -> Unit) {
-        mockBehaviorOverride()
+    private fun testTakingOverPermissionAndPermissionGroupDefinitions(
+        oldPermissionOwnerIsSystem: Boolean = false,
+        newPermissionOwnerIsSystem: Boolean = false,
+        newPermissionOwnerIsInstant: Boolean = false,
+        permissionGroupAlreadyExists: Boolean = true,
+        permissionAlreadyExists: Boolean = true,
+        type: Int = Permission.TYPE_MANIFEST,
+        isReconciled: Boolean = true,
+    ) {
+        val oldPermissionOwnerPackageState = mockPackageState(
+            APP_ID_1,
+            PACKAGE_NAME_1,
+            isSystem = oldPermissionOwnerIsSystem
+        )
+        addPackageState(oldPermissionOwnerPackageState)
+        if (permissionGroupAlreadyExists) {
+            addPermissionGroup(mockParsedPermissionGroup(PERMISSION_GROUP_NAME_0, PACKAGE_NAME_1))
+        }
+        if (permissionAlreadyExists) {
+            addPermission(
+                mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1),
+                type = type,
+                isReconciled = isReconciled
+            )
+        }
 
         mutateState {
+            val newPermissionOwnerPackageState = mockPackageState(
+                APP_ID_0,
+                mockSimpleAndroidPackage(),
+                isSystem = newPermissionOwnerIsSystem,
+                isInstantApp = newPermissionOwnerIsInstant
+            )
+            addPackageState(newPermissionOwnerPackageState, newState)
             with(appIdPermissionPolicy) {
-                onPackageAdded(packageState0)
+                onPackageAdded(newPermissionOwnerPackageState)
             }
         }
     }
 
+    @Test
+    fun testOnPackageAdded_permissionGroupChanged_getsRevoked() {
+        testPermissionChanged(
+            oldPermissionGroup = PERMISSION_GROUP_NAME_1,
+            newPermissionGroup = PERMISSION_GROUP_NAME_0
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that has a permission group change" +
+                " for a permission it defines, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_protectionLevelChanged_getsRevoked() {
+        testPermissionChanged(newProtectionLevel = PermissionInfo.PROTECTION_INTERNAL)
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that has a protection level change" +
+                " for a permission it defines, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    private fun testPermissionChanged(
+        oldPermissionGroup: String? = null,
+        newPermissionGroup: String? = null,
+        newProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS
+    ) {
+        val oldPermission = mockParsedPermission(
+            PERMISSION_NAME_0,
+            PACKAGE_NAME_0,
+            group = oldPermissionGroup,
+            protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
+        )
+        val oldPackageState = mockPackageState(
+            APP_ID_0,
+            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldPermission))
+        )
+        addPackageState(oldPackageState)
+        addPermission(oldPermission)
+        setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED)
+
+        mutateState {
+            val newPermission = mockParsedPermission(
+                PERMISSION_NAME_0,
+                PACKAGE_NAME_0,
+                group = newPermissionGroup,
+                protectionLevel = newProtectionLevel
+            )
+            val newPackageState = mockPackageState(
+                APP_ID_0,
+                mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newPermission))
+            )
+            addPackageState(newPackageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(newPackageState)
+            }
+        }
+    }
+
+    @Test
+    fun testOnPackageAdded_permissionTreeNoLongerDeclared_getsDefinitionRemoved() {
+        testPermissionDeclaration {}
+
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that no longer defines a permission" +
+                " tree, the permission tree: $PERMISSION_NAME_0 in system state should be removed"
+        )
+            .that(getPermissionTree(PERMISSION_NAME_0))
+            .isNull()
+    }
+
+    @Test
+    fun testOnPackageAdded_permissionTreeByDisabledSystemPackage_remainsUnchanged() {
+        testPermissionDeclaration {
+            val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+            addDisabledSystemPackageState(disabledSystemPackageState)
+        }
+
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that no longer defines" +
+                " a permission tree while this permission tree is still defined by" +
+                " a disabled system package, the permission tree: $PERMISSION_NAME_0 in" +
+                " system state should not be removed"
+        )
+            .that(getPermissionTree(PERMISSION_TREE_NAME))
+            .isNotNull()
+    }
+
+    @Test
+    fun testOnPackageAdded_permissionNoLongerDeclared_getsDefinitionRemoved() {
+        testPermissionDeclaration {}
+
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that no longer defines a permission," +
+                " the permission: $PERMISSION_NAME_0 in system state should be removed"
+        )
+            .that(getPermission(PERMISSION_NAME_0))
+            .isNull()
+    }
+
+    @Test
+    fun testOnPackageAdded_permissionByDisabledSystemPackage_remainsUnchanged() {
+        testPermissionDeclaration {
+            val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+            addDisabledSystemPackageState(disabledSystemPackageState)
+        }
+
+        assertWithMessage(
+            "After onPackageAdded() is called for a disabled system package and it's updated apk" +
+                " no longer defines a permission, the permission: $PERMISSION_NAME_0 in" +
+                " system state should not be removed"
+        )
+            .that(getPermission(PERMISSION_NAME_0))
+            .isNotNull()
+    }
+
+    private fun testPermissionDeclaration(additionalSetup: () -> Unit) {
+        val oldPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+        addPackageState(oldPackageState)
+        addPermission(defaultPermissionTree)
+        addPermission(defaultPermission)
+
+        additionalSetup()
+
+        mutateState {
+            val newPackageState = mockPackageState(APP_ID_0, mockAndroidPackage(PACKAGE_NAME_0))
+            addPackageState(newPackageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(newPackageState)
+            }
+        }
+    }
+
+    @Test
+    fun testOnPackageAdded_permissionsNoLongerRequested_getsFlagsRevoked() {
+        val parsedPermission = mockParsedPermission(
+            PERMISSION_NAME_0,
+            PACKAGE_NAME_0,
+            protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
+        )
+        val oldPackageState = mockPackageState(
+            APP_ID_0,
+            mockAndroidPackage(
+                PACKAGE_NAME_0,
+                permissions = listOf(parsedPermission),
+                requestedPermissions = setOf(PERMISSION_NAME_0)
+            )
+        )
+        addPackageState(oldPackageState)
+        addPermission(parsedPermission)
+        setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED)
+
+        mutateState {
+            val newPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+            addPackageState(newPackageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(newPackageState)
+            }
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that no longer requests a permission" +
+                " the actual permission flags $actualFlags should match the" +
+                " expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_storageAndMediaPermissionsDowngradingPastQ_getsRuntimeRevoked() {
+        testRevokePermissionsOnPackageUpdate(
+            PermissionFlags.RUNTIME_GRANTED,
+            newTargetSdkVersion = Build.VERSION_CODES.P
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that's downgrading past Q" +
+                " the actual permission flags $actualFlags should match the" +
+                " expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_storageAndMediaPermissionsNotDowngradingPastQ_remainsUnchanged() {
+        val oldFlags = PermissionFlags.RUNTIME_GRANTED
+        testRevokePermissionsOnPackageUpdate(
+            oldFlags,
+            oldTargetSdkVersion = Build.VERSION_CODES.P,
+            newTargetSdkVersion = Build.VERSION_CODES.P
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that's not downgrading past Q" +
+                " the actual permission flags $actualFlags should match the" +
+                " expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_policyFixedDowngradingPastQ_remainsUnchanged() {
+        val oldFlags = PermissionFlags.RUNTIME_GRANTED and PermissionFlags.POLICY_FIXED
+        testRevokePermissionsOnPackageUpdate(oldFlags, newTargetSdkVersion = Build.VERSION_CODES.P)
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that's downgrading past Q" +
+                " the actual permission flags with PermissionFlags.POLICY_FIXED $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_newlyRequestingLegacyExternalStorage_getsRuntimeRevoked() {
+        testRevokePermissionsOnPackageUpdate(
+            PermissionFlags.RUNTIME_GRANTED,
+            oldTargetSdkVersion = Build.VERSION_CODES.P,
+            newTargetSdkVersion = Build.VERSION_CODES.P,
+            oldIsRequestLegacyExternalStorage = false
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package with" +
+                " newlyRequestingLegacyExternalStorage, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_missingOldPackage_remainsUnchanged() {
+        val oldFlags = PermissionFlags.RUNTIME_GRANTED
+        testRevokePermissionsOnPackageUpdate(
+            oldFlags,
+            newTargetSdkVersion = Build.VERSION_CODES.P,
+            isOldPackageMissing = true
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that's downgrading past Q" +
+                " and doesn't have the oldPackage, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    private fun testRevokePermissionsOnPackageUpdate(
+        oldFlags: Int,
+        oldTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+        newTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+        oldIsRequestLegacyExternalStorage: Boolean = true,
+        newIsRequestLegacyExternalStorage: Boolean = true,
+        isOldPackageMissing: Boolean = false
+    ) {
+        val parsedPermission = mockParsedPermission(
+            PERMISSION_READ_EXTERNAL_STORAGE,
+            PACKAGE_NAME_0,
+            protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
+        )
+        val oldPackageState = if (isOldPackageMissing) {
+            mockPackageState(APP_ID_0, PACKAGE_NAME_0)
+        } else {
+            mockPackageState(
+                APP_ID_0,
+                mockAndroidPackage(
+                    PACKAGE_NAME_0,
+                    targetSdkVersion = oldTargetSdkVersion,
+                    isRequestLegacyExternalStorage = oldIsRequestLegacyExternalStorage,
+                    requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE),
+                    permissions = listOf(parsedPermission)
+                )
+            )
+        }
+        addPackageState(oldPackageState)
+        addPermission(parsedPermission)
+        setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE, oldFlags)
+
+        mutateState {
+            val newPackageState = mockPackageState(
+                APP_ID_0,
+                mockAndroidPackage(
+                    PACKAGE_NAME_0,
+                    targetSdkVersion = newTargetSdkVersion,
+                    isRequestLegacyExternalStorage = newIsRequestLegacyExternalStorage,
+                    requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE),
+                    permissions = listOf(parsedPermission)
+                )
+            )
+            addPackageState(newPackageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(newPackageState)
+            }
+        }
+    }
+
+    @Test
+    fun testOnPackageAdded_normalPermissionAlreadyGranted_remainsUnchanged() {
+        val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.INSTALL_REVOKED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a normal permission" +
+                " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_normalPermissionNotInstallRevoked_getsGranted() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_NORMAL,
+            isNewInstall = true
+        ) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a normal permission" +
+                " with no existing flags, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_normalPermissionRequestedByInstalledPackage_getsGranted() {
+        val oldFlags = PermissionFlags.INSTALL_REVOKED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a normal permission" +
+                " with the INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags since it's a new install"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    /**
+     * We setup a permission protection level change from SIGNATURE to NORMAL in order to make
+     * the permission a "changed permission" in order to test evaluatePermissionState() called by
+     * evaluatePermissionStateForAllPackages(). This makes the requestingPackageState not the
+     * installedPackageState so that we can test whether requesting by system package will give us
+     * the expected permission flags.
+     *
+     * Besides, this also helps us test evaluatePermissionStateForAllPackages(). Since both
+     * evaluatePermissionStateForAllPackages() and evaluateAllPermissionStatesForPackage() call
+     * evaluatePermissionState() in their implementations, we use these tests as the only tests
+     * that test evaluatePermissionStateForAllPackages()
+     */
+    @Test
+    fun testOnPackageAdded_normalPermissionRequestedBySystemPackage_getsGranted() {
+        testEvaluateNormalPermissionStateWithPermissionChanges(requestingPackageIsSystem = true)
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a system package that requests a normal" +
+                " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_normalCompatibilityPermission_getsGranted() {
+        testEvaluateNormalPermissionStateWithPermissionChanges(
+            permissionName = PERMISSION_POST_NOTIFICATIONS,
+            requestingPackageTargetSdkVersion = Build.VERSION_CODES.S
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS)
+        val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a normal compatibility" +
+                " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_normalPermissionPreviouslyRevoked_getsInstallRevoked() {
+        testEvaluateNormalPermissionStateWithPermissionChanges()
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.INSTALL_REVOKED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a normal" +
+                " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+                " should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    private fun testEvaluateNormalPermissionStateWithPermissionChanges(
+        permissionName: String = PERMISSION_NAME_0,
+        requestingPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+        requestingPackageIsSystem: Boolean = false
+    ) {
+        val oldParsedPermission = mockParsedPermission(
+            permissionName,
+            PACKAGE_NAME_0,
+            protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
+        )
+        val oldPermissionOwnerPackageState = mockPackageState(
+            APP_ID_0,
+            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldParsedPermission))
+        )
+        val requestingPackageState = mockPackageState(
+            APP_ID_1,
+            mockAndroidPackage(
+                PACKAGE_NAME_1,
+                requestedPermissions = setOf(permissionName),
+                targetSdkVersion = requestingPackageTargetSdkVersion
+            ),
+            isSystem = requestingPackageIsSystem,
+        )
+        addPackageState(oldPermissionOwnerPackageState)
+        addPackageState(requestingPackageState)
+        addPermission(oldParsedPermission)
+        val oldFlags = PermissionFlags.INSTALL_REVOKED
+        setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags)
+
+        mutateState {
+            val newPermissionOwnerPackageState = mockPackageState(
+                APP_ID_0,
+                mockAndroidPackage(
+                    PACKAGE_NAME_0,
+                    permissions = listOf(mockParsedPermission(permissionName, PACKAGE_NAME_0))
+                )
+            )
+            addPackageState(newPermissionOwnerPackageState, newState)
+            with(appIdPermissionPolicy) {
+                onPackageAdded(newPermissionOwnerPackageState)
+            }
+        }
+    }
+
+    @Test
+    fun testOnPackageAdded_normalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
+        val oldFlags = PermissionFlags.ROLE or PermissionFlags.USER_SET
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP
+        ) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.INSTALL_GRANTED or oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a normal app op" +
+                " permission with existing ROLE and USER_SET flags, the actual permission flags" +
+                " $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_internalPermissionWasGrantedWithMissingPackage_getsProtectionGranted() {
+        val oldFlags = PermissionFlags.PROTECTION_GRANTED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_INTERNAL) {
+            val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+            addPackageState(packageStateWithMissingPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests an internal permission" +
+                " with missing android package and $oldFlags flag, the actual permission flags" +
+                " $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_internalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
+        val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
+            PermissionFlags.USER_SET
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP
+        ) {
+            val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+            addPackageState(packageStateWithMissingPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests an internal permission" +
+                " with missing android package and $oldFlags flag and the permission isAppOp," +
+                " the actual permission flags $actualFlags should match the expected" +
+                " flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_internalDevelopmentPermission_getsRuntimeGrantedPreserved() {
+        val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT
+        ) {
+            val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+            addPackageState(packageStateWithMissingPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests an internal permission" +
+                " with missing android package and $oldFlags flag and permission isDevelopment," +
+                " the actual permission flags $actualFlags should match the expected" +
+                " flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_internalRolePermission_getsRoleAndRuntimeGrantedPreserved() {
+        val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
+            PermissionFlags.RUNTIME_GRANTED
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE
+        ) {
+            val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+            addPackageState(packageStateWithMissingPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests an internal permission" +
+                " with missing android package and $oldFlags flag and the permission isRole," +
+                " the actual permission flags $actualFlags should match the expected" +
+                " flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_signaturePrivilegedPermissionNotAllowlisted_isNotGranted() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED,
+            isInstalledPackageSystem = true,
+            isInstalledPackagePrivileged = true,
+            isInstalledPackageProduct = true,
+            // To mock the return value of shouldGrantPrivilegedOrOemPermission()
+            isInstalledPackageVendor = true,
+            isNewInstall = true
+        ) {
+            val platformPackage = mockPackageState(
+                PLATFORM_APP_ID,
+                mockAndroidPackage(PLATFORM_PACKAGE_NAME)
+            )
+            setupAllowlist(PACKAGE_NAME_1, false)
+            addPackageState(platformPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a signature privileged" +
+                " permission that's not allowlisted, the actual permission" +
+                " flags $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_nonPrivilegedPermissionShouldGrantBySignature_getsProtectionGranted() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_SIGNATURE,
+            isInstalledPackageSystem = true,
+            isInstalledPackagePrivileged = true,
+            isInstalledPackageProduct = true,
+            isInstalledPackageSignatureMatching = true,
+            isInstalledPackageVendor = true,
+            isNewInstall = true
+        ) {
+            val platformPackage = mockPackageState(
+                PLATFORM_APP_ID,
+                mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true)
+            )
+            setupAllowlist(PACKAGE_NAME_1, false)
+            addPackageState(platformPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a signature" +
+                " non-privileged permission, the actual permission" +
+                " flags $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_privilegedAllowlistPermissionShouldGrantByProtectionFlags_getsGranted() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED,
+            isInstalledPackageSystem = true,
+            isInstalledPackagePrivileged = true,
+            isInstalledPackageProduct = true,
+            isNewInstall = true
+        ) {
+            val platformPackage = mockPackageState(
+                PLATFORM_APP_ID,
+                mockAndroidPackage(PLATFORM_PACKAGE_NAME)
+            )
+            setupAllowlist(PACKAGE_NAME_1, true)
+            addPackageState(platformPackage)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a signature privileged" +
+                " permission that's allowlisted and should grant by protection flags, the actual" +
+                " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    private fun setupAllowlist(
+        packageName: String,
+        allowlistState: Boolean,
+        state: MutableAccessState = oldState
+    ) {
+        state.mutateExternalState().setPrivilegedPermissionAllowlistPackages(
+            MutableIndexedListSet<String>().apply { add(packageName) }
+        )
+        val mockAllowlist = mock<PermissionAllowlist> {
+            whenever(
+                getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0)
+            ).thenReturn(allowlistState)
+        }
+        state.mutateExternalState().setPermissionAllowlist(mockAllowlist)
+    }
+
+    @Test
+    fun testOnPackageAdded_nonRuntimeFlagsOnRuntimePermissions_getsCleared() {
+        val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.PREGRANT or
+            PermissionFlags.RUNTIME_GRANTED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.PREGRANT or PermissionFlags.RUNTIME_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " with existing $oldFlags flags, the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_newPermissionsForPreM_requiresUserReview() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP,
+            isNewInstall = true
+        ) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " with no existing flags in pre M, actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_legacyOrImplicitGrantedPermissionPreviouslyRevoked_getsAppOpRevoked() {
+        val oldFlags = PermissionFlags.USER_FIXED
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP
+        ) {
+            setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.USER_FIXED or
+            PermissionFlags.APP_OP_REVOKED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," +
+                " the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_legacyGrantedPermissionsForPostM_userReviewRequirementRemoved() {
+        val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " that used to require user review, the user review requirement should be removed" +
+                " if it's upgraded to post M. The actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_legacyGrantedPermissionsAlreadyReviewedForPostM_getsGranted() {
+        val oldFlags = PermissionFlags.LEGACY_GRANTED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" +
+                " if it's upgraded to post M. The actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_leanbackNotificationPermissionsForPostM_getsImplicitGranted() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            permissionName = PERMISSION_POST_NOTIFICATIONS,
+            isNewInstall = true
+        ) {
+            oldState.mutateExternalState().setLeanback(true)
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS)
+        val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime notification" +
+                " permission when isLeanback, the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_implicitSourceFromNonRuntime_getsImplicitGranted() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            implicitPermissions = setOf(PERMISSION_NAME_0),
+            isNewInstall = true
+        ) {
+            oldState.mutateExternalState().setImplicitToSourcePermissions(
+                MutableIndexedMap<String, IndexedListSet<String>>().apply {
+                    put(PERMISSION_NAME_0, MutableIndexedListSet<String>().apply {
+                        add(PERMISSION_NAME_1)
+                    })
+                }
+            )
+            addPermission(mockParsedPermission(PERMISSION_NAME_1, PACKAGE_NAME_0))
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime implicit" +
+                " permission that's source from a non-runtime permission, the actual permission" +
+                " flags $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    /**
+     * For a legacy granted or implicit permission during the app upgrade, when the permission
+     * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag
+     * so that the app can request the permission.
+     */
+    @Test
+    fun testOnPackageAdded_noLongerLegacyOrImplicitGranted_canBeRequested() {
+        val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.APP_OP_REVOKED or
+            PermissionFlags.RUNTIME_GRANTED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" +
+                " flags $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_noLongerImplicitPermissions_getsRuntimeAndImplicitFlagsRemoved() {
+        val oldFlags = PermissionFlags.IMPLICIT or PermissionFlags.RUNTIME_GRANTED or
+            PermissionFlags.USER_SET or PermissionFlags.USER_FIXED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = 0
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " that is no longer implicit and we shouldn't retain as nearby device" +
+                " permissions, the actual permission flags $actualFlags should match the expected" +
+                " flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_noLongerImplicitNearbyPermissionsWasGranted_getsRuntimeGranted() {
+        val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            permissionName = PERMISSION_BLUETOOTH_CONNECT,
+            requestedPermissions = setOf(
+                PERMISSION_BLUETOOTH_CONNECT,
+                PERMISSION_ACCESS_BACKGROUND_LOCATION
+            )
+        ) {
+            setPermissionFlags(
+                APP_ID_1,
+                USER_ID_0,
+                PERMISSION_ACCESS_BACKGROUND_LOCATION,
+                PermissionFlags.RUNTIME_GRANTED
+            )
+        }
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_BLUETOOTH_CONNECT)
+        val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime nearby device" +
+                " permission that was granted by implicit, the actual permission flags" +
+                " $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_noLongerImplicitSystemOrPolicyFixedWasGranted_getsRuntimeGranted() {
+        val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT or
+            PermissionFlags.SYSTEM_FIXED
+        testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.SYSTEM_FIXED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime permission" +
+                " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," +
+                " the actual permission flags $actualFlags should match the expected" +
+                " flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_restrictedPermissionsNotExempt_getsRestrictionFlags() {
+        val oldFlags = PermissionFlags.RESTRICTION_REVOKED
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
+        ) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldFlags
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime hard" +
+                " restricted permission that is not exempted, the actual permission flags" +
+                " $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
+        val oldFlags = 0
+        testEvaluatePermissionState(
+            oldFlags,
+            PermissionInfo.PROTECTION_DANGEROUS,
+            permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+        ) {}
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.UPGRADE_EXEMPT
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a runtime soft" +
+                " restricted permission that is exempted, the actual permission flags" +
+                " $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_runtimeExistingImplicitPermissions_sourceFlagsNotInherited() {
+        val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED
+        testInheritImplicitPermissionStates(
+            implicitPermissionFlags = oldImplicitPermissionFlags,
+            isNewInstallAndNewPermission = false
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or
+            PermissionFlags.APP_OP_REVOKED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a permission that is" +
+                " implicit, existing and runtime, it should not inherit the runtime flags from" +
+                " the source permission. Hence the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_nonRuntimeNewImplicitPermissions_sourceFlagsNotInherited() {
+        testInheritImplicitPermissionStates(
+            implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a permission that is" +
+                " implicit, new and non-runtime, it should not inherit the runtime flags from" +
+                " the source permission. Hence the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_runtimeNewImplicitPermissions_sourceFlagsInherited() {
+        val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+        testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags)
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or
+            PermissionFlags.IMPLICIT
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a permission that is" +
+                " implicit, new and runtime, it should inherit the runtime flags from" +
+                " the source permission. Hence the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    @Test
+    fun testOnPackageAdded_grantingNewFromRevokeImplicitPermissions_onlySourceFlagsInherited() {
+        val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+        testInheritImplicitPermissionStates(
+            implicitPermissionFlags = PermissionFlags.POLICY_FIXED,
+            sourceRuntimeFlags = sourceRuntimeFlags,
+            isAnySourcePermissionNonRuntime = false
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+        val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a permission that is" +
+                " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
+                " from source permission. Hence the actual permission flags $actualFlags should" +
+                " match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    /**
+     * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to
+     * remove the IMPLICIT flag so that they will be granted when they are no longer implicit.
+     * (instead of revoking it)
+     */
+    @Test
+    fun testOnPackageAdded_mediaImplicitPermissions_getsImplicitFlagRemoved() {
+        val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+        testInheritImplicitPermissionStates(
+            implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION,
+            sourceRuntimeFlags = sourceRuntimeFlags
+        )
+
+        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_ACCESS_MEDIA_LOCATION)
+        val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED
+        assertWithMessage(
+            "After onPackageAdded() is called for a package that requests a media permission that" +
+                " is implicit, new and runtime, it should inherit the runtime flags from" +
+                " the source permission and have the IMPLICIT flag removed. Hence the actual" +
+                " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+        )
+            .that(actualFlags)
+            .isEqualTo(expectedNewFlags)
+    }
+
+    private fun testInheritImplicitPermissionStates(
+        implicitPermissionName: String = PERMISSION_NAME_0,
+        implicitPermissionFlags: Int = 0,
+        implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS,
+        sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET,
+        isAnySourcePermissionNonRuntime: Boolean = true,
+        isNewInstallAndNewPermission: Boolean = true
+    ) {
+        val implicitPermission = mockParsedPermission(
+            implicitPermissionName,
+            PACKAGE_NAME_0,
+            protectionLevel = implicitPermissionProtectionLevel,
+        )
+        // For source from non-runtime in order to grant by implicit
+        val sourcePermission1 = mockParsedPermission(
+            PERMISSION_NAME_1,
+            PACKAGE_NAME_0,
+            protectionLevel = if (isAnySourcePermissionNonRuntime) {
+                PermissionInfo.PROTECTION_NORMAL
+            } else {
+                PermissionInfo.PROTECTION_DANGEROUS
+            }
+        )
+        // For inheriting runtime flags
+        val sourcePermission2 = mockParsedPermission(
+            PERMISSION_NAME_2,
+            PACKAGE_NAME_0,
+            protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+        )
+        val permissionOwnerPackageState = mockPackageState(
+            APP_ID_0,
+            mockAndroidPackage(
+                PACKAGE_NAME_0,
+                permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2)
+            )
+        )
+        val installedPackageState = mockPackageState(
+            APP_ID_1,
+            mockAndroidPackage(
+                PACKAGE_NAME_1,
+                requestedPermissions = setOf(
+                    implicitPermissionName,
+                    PERMISSION_NAME_1,
+                    PERMISSION_NAME_2
+                ),
+                implicitPermissions = setOf(implicitPermissionName)
+            )
+        )
+        oldState.mutateExternalState().setImplicitToSourcePermissions(
+            MutableIndexedMap<String, IndexedListSet<String>>().apply {
+                put(implicitPermissionName, MutableIndexedListSet<String>().apply {
+                    add(PERMISSION_NAME_1)
+                    add(PERMISSION_NAME_2)
+                })
+            }
+        )
+        addPackageState(permissionOwnerPackageState)
+        addPermission(implicitPermission)
+        addPermission(sourcePermission1)
+        addPermission(sourcePermission2)
+        if (!isNewInstallAndNewPermission) {
+            addPackageState(installedPackageState)
+            setPermissionFlags(APP_ID_1, USER_ID_0, implicitPermissionName, implicitPermissionFlags)
+        }
+        setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_2, sourceRuntimeFlags)
+
+        mutateState {
+            if (isNewInstallAndNewPermission) {
+                addPackageState(installedPackageState)
+                setPermissionFlags(
+                    APP_ID_1,
+                    USER_ID_0,
+                    implicitPermissionName,
+                    implicitPermissionFlags,
+                    newState
+                )
+            }
+            with(appIdPermissionPolicy) {
+                onPackageAdded(installedPackageState)
+            }
+        }
+    }
+
+    /**
+     * Setup simple package states for testing evaluatePermissionState().
+     * permissionOwnerPackageState is definer of permissionName with APP_ID_0.
+     * installedPackageState is the installed package that requests permissionName with APP_ID_1.
+     *
+     * @param oldFlags the existing permission flags for APP_ID_1, USER_ID_0, permissionName
+     * @param protectionLevel the protectionLevel for the permission
+     * @param permissionName the name of the permission (1) being defined (2) of the oldFlags, and
+     *                       (3) requested by installedPackageState
+     * @param requestedPermissions the permissions requested by installedPackageState
+     * @param implicitPermissions the implicit permissions of installedPackageState
+     * @param permissionInfoFlags the flags for the permission itself
+     * @param isInstalledPackageSystem whether installedPackageState is a system package
+     *
+     * @return installedPackageState
+     */
+    fun testEvaluatePermissionState(
+        oldFlags: Int,
+        protectionLevel: Int,
+        permissionName: String = PERMISSION_NAME_0,
+        requestedPermissions: Set<String> = setOf(permissionName),
+        implicitPermissions: Set<String> = emptySet(),
+        permissionInfoFlags: Int = 0,
+        isInstalledPackageSystem: Boolean = false,
+        isInstalledPackagePrivileged: Boolean = false,
+        isInstalledPackageProduct: Boolean = false,
+        isInstalledPackageSignatureMatching: Boolean = false,
+        isInstalledPackageVendor: Boolean = false,
+        installedPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+        isNewInstall: Boolean = false,
+        additionalSetup: () -> Unit
+    ) {
+        val parsedPermission = mockParsedPermission(
+            permissionName,
+            PACKAGE_NAME_0,
+            protectionLevel = protectionLevel,
+            flags = permissionInfoFlags
+        )
+        val permissionOwnerPackageState = mockPackageState(
+            APP_ID_0,
+            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+        )
+        val installedPackageState = mockPackageState(
+            APP_ID_1,
+            mockAndroidPackage(
+                PACKAGE_NAME_1,
+                requestedPermissions = requestedPermissions,
+                implicitPermissions = implicitPermissions,
+                targetSdkVersion = installedPackageTargetSdkVersion,
+                isSignatureMatching = isInstalledPackageSignatureMatching
+            ),
+            isSystem = isInstalledPackageSystem,
+            isPrivileged = isInstalledPackagePrivileged,
+            isProduct = isInstalledPackageProduct,
+            isVendor = isInstalledPackageVendor
+        )
+        addPackageState(permissionOwnerPackageState)
+        if (!isNewInstall) {
+            addPackageState(installedPackageState)
+            setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags)
+        }
+        addPermission(parsedPermission)
+
+        additionalSetup()
+
+        mutateState {
+            if (isNewInstall) {
+                addPackageState(installedPackageState, newState)
+                setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags, newState)
+            }
+            with(appIdPermissionPolicy) {
+                onPackageAdded(installedPackageState)
+            }
+        }
+    }
+
+    /**
+     * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
+     */
+    private fun mockSimpleAndroidPackage(): AndroidPackage =
+        mockAndroidPackage(
+            PACKAGE_NAME_0,
+            permissionGroups = listOf(defaultPermissionGroup),
+            permissions = listOf(defaultPermissionTree, defaultPermission)
+        )
+
     private inline fun mutateState(action: MutateStateScope.() -> Unit) {
         newState = oldState.toMutable()
         MutateStateScope(oldState, newState).action()
     }
 
-    private fun createSystemStatePermission(
+    private fun mockPackageState(
         appId: Int,
         packageName: String,
+        isSystem: Boolean = false,
+    ): PackageState =
+        mock {
+            whenever(this.appId).thenReturn(appId)
+            whenever(this.packageName).thenReturn(packageName)
+            whenever(androidPackage).thenReturn(null)
+            whenever(this.isSystem).thenReturn(isSystem)
+        }
+
+    private fun mockPackageState(
+        appId: Int,
+        androidPackage: AndroidPackage,
+        isSystem: Boolean = false,
+        isPrivileged: Boolean = false,
+        isProduct: Boolean = false,
+        isInstantApp: Boolean = false,
+        isVendor: Boolean = false
+    ): PackageState =
+        mock {
+            whenever(this.appId).thenReturn(appId)
+            whenever(this.androidPackage).thenReturn(androidPackage)
+            val packageName = androidPackage.packageName
+            whenever(this.packageName).thenReturn(packageName)
+            whenever(this.isSystem).thenReturn(isSystem)
+            whenever(this.isPrivileged).thenReturn(isPrivileged)
+            whenever(this.isProduct).thenReturn(isProduct)
+            whenever(this.isVendor).thenReturn(isVendor)
+            val userStates = SparseArray<PackageUserState>().apply {
+                put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) })
+            }
+            whenever(this.userStates).thenReturn(userStates)
+        }
+
+    private fun mockAndroidPackage(
+        packageName: String,
+        targetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+        isRequestLegacyExternalStorage: Boolean = false,
+        adoptPermissions: List<String> = emptyList(),
+        implicitPermissions: Set<String> = emptySet(),
+        requestedPermissions: Set<String> = emptySet(),
+        permissionGroups: List<ParsedPermissionGroup> = emptyList(),
+        permissions: List<ParsedPermission> = emptyList(),
+        isSignatureMatching: Boolean = false
+    ): AndroidPackage =
+        mock {
+            whenever(this.packageName).thenReturn(packageName)
+            whenever(this.targetSdkVersion).thenReturn(targetSdkVersion)
+            whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage)
+            whenever(this.adoptPermissions).thenReturn(adoptPermissions)
+            whenever(this.implicitPermissions).thenReturn(implicitPermissions)
+            whenever(this.requestedPermissions).thenReturn(requestedPermissions)
+            whenever(this.permissionGroups).thenReturn(permissionGroups)
+            whenever(this.permissions).thenReturn(permissions)
+            val signingDetails = mock<SigningDetails> {
+                whenever(
+                    hasCommonSignerWithCapability(any(), any())
+                ).thenReturn(isSignatureMatching)
+                whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching)
+                whenever(
+                    checkCapability(any<SigningDetails>(), any())
+                ).thenReturn(isSignatureMatching)
+            }
+            whenever(this.signingDetails).thenReturn(signingDetails)
+        }
+
+    private fun mockParsedPermission(
         permissionName: String,
-        protectionLevel: Int,
+        packageName: String,
+        backgroundPermission: String? = null,
+        group: String? = null,
+        protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL,
+        flags: Int = 0,
+        isTree: Boolean = false
+    ): ParsedPermission =
+        mock {
+            whenever(name).thenReturn(permissionName)
+            whenever(this.packageName).thenReturn(packageName)
+            whenever(metaData).thenReturn(Bundle())
+            whenever(this.backgroundPermission).thenReturn(backgroundPermission)
+            whenever(this.group).thenReturn(group)
+            whenever(this.protectionLevel).thenReturn(protectionLevel)
+            whenever(this.flags).thenReturn(flags)
+            whenever(this.isTree).thenReturn(isTree)
+        }
+
+    private fun mockParsedPermissionGroup(
+        permissionGroupName: String,
+        packageName: String,
+    ): ParsedPermissionGroup =
+        mock {
+            whenever(name).thenReturn(permissionGroupName)
+            whenever(this.packageName).thenReturn(packageName)
+            whenever(metaData).thenReturn(Bundle())
+        }
+
+    private fun addPackageState(packageState: PackageState, state: MutableAccessState = oldState) {
+        state.mutateExternalState().apply {
+            setPackageStates(
+                packageStates.toMutableMap().apply {
+                    put(packageState.packageName, packageState)
+                }
+            )
+            mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
+                .add(packageState.packageName)
+        }
+    }
+
+    private fun addDisabledSystemPackageState(
+        packageState: PackageState,
+        state: MutableAccessState = oldState
+    ) = state.mutateExternalState().apply {
+        (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState
+    }
+
+    private fun addPermission(
+        parsedPermission: ParsedPermission,
         type: Int = Permission.TYPE_MANIFEST,
         isReconciled: Boolean = true,
-        isTree: Boolean = false
+        state: MutableAccessState = oldState
     ) {
-        @Suppress("DEPRECATION")
-        val permissionInfo = PermissionInfo().apply {
-            name = permissionName
-            this.packageName = packageName
-            this.protectionLevel = protectionLevel
-        }
+        val permissionInfo = PackageInfoUtils.generatePermissionInfo(
+            parsedPermission,
+            PackageManager.GET_META_DATA.toLong()
+        )!!
+        val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId
         val permission = Permission(permissionInfo, isReconciled, type, appId)
-        if (isTree) {
-            oldState.mutateSystemState().mutatePermissionTrees().put(permissionName, permission)
+        if (parsedPermission.isTree) {
+            state.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
         } else {
-            oldState.mutateSystemState().mutatePermissions().put(permissionName, permission)
+            state.mutateSystemState().mutatePermissions()[permission.name] = permission
         }
     }
 
-    private fun createSystemStatePermissionGroup(packageName: String, permissionGroupName: String) {
-        @Suppress("DEPRECATION")
-        val permissionGroupInfo = PermissionGroupInfo().apply {
-            name = permissionGroupName
-            this.packageName = packageName
-        }
-        oldState.mutateSystemState().mutatePermissionGroups()[permissionGroupName] =
-            permissionGroupInfo
+    private fun addPermissionGroup(
+        parsedPermissionGroup: ParsedPermissionGroup,
+        state: MutableAccessState = oldState
+    ) {
+        state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] =
+            PackageInfoUtils.generatePermissionGroupInfo(
+                parsedPermissionGroup,
+                PackageManager.GET_META_DATA.toLong()
+            )!!
     }
 
-    fun getPermissionFlags(
+    private fun getPermission(
+        permissionName: String,
+        state: MutableAccessState = newState
+    ): Permission? = state.systemState.permissions[permissionName]
+
+    private fun getPermissionTree(
+        permissionTreeName: String,
+        state: MutableAccessState = newState
+    ): Permission? = state.systemState.permissionTrees[permissionTreeName]
+
+    private fun getPermissionGroup(
+        permissionGroupName: String,
+        state: MutableAccessState = newState
+    ): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName]
+
+    private fun getPermissionFlags(
         appId: Int,
         userId: Int,
         permissionName: String,
@@ -517,20 +1892,46 @@
     ): Int =
         state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
 
+    private fun setPermissionFlags(
+        appId: Int,
+        userId: Int,
+        permissionName: String,
+        flags: Int,
+        state: MutableAccessState = oldState
+    ) =
+        state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) {
+            MutableIndexedMap()
+        }.put(permissionName, flags)
+
     companion object {
         private const val PACKAGE_NAME_0 = "packageName0"
         private const val PACKAGE_NAME_1 = "packageName1"
+        private const val MISSING_ANDROID_PACKAGE = "missingAndroidPackage"
+        private const val PLATFORM_PACKAGE_NAME = "android"
 
         private const val APP_ID_0 = 0
         private const val APP_ID_1 = 1
-
-        private const val PERMISSION_NAME_0 = "permissionName0"
-        private const val PERMISSION_NAME_1 = "permissionName1"
+        private const val PLATFORM_APP_ID = 2
 
         private const val PERMISSION_GROUP_NAME_0 = "permissionGroupName0"
         private const val PERMISSION_GROUP_NAME_1 = "permissionGroupName1"
 
+        private const val PERMISSION_TREE_NAME = "permissionTree"
+
+        private const val PERMISSION_NAME_0 = "permissionName0"
+        private const val PERMISSION_NAME_1 = "permissionName1"
+        private const val PERMISSION_NAME_2 = "permissionName2"
+        private const val PERMISSION_READ_EXTERNAL_STORAGE =
+            Manifest.permission.READ_EXTERNAL_STORAGE
+        private const val PERMISSION_POST_NOTIFICATIONS =
+            Manifest.permission.POST_NOTIFICATIONS
+        private const val PERMISSION_BLUETOOTH_CONNECT =
+            Manifest.permission.BLUETOOTH_CONNECT
+        private const val PERMISSION_ACCESS_BACKGROUND_LOCATION =
+            Manifest.permission.ACCESS_BACKGROUND_LOCATION
+        private const val PERMISSION_ACCESS_MEDIA_LOCATION =
+            Manifest.permission.ACCESS_MEDIA_LOCATION
+
         private const val USER_ID_0 = 0
-        private const val USER_ID_1 = 1
     }
 }
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 82d00a6..7374901 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -44,6 +44,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.server.display.config.HdrBrightnessData;
 import com.android.server.display.config.ThermalStatus;
 
 import org.junit.Before;
@@ -477,6 +478,23 @@
                 mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(), ZERO_DELTA);
     }
 
+    @Test
+    public void testHdrBrightnessDataFromDisplayConfig() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        HdrBrightnessData data = mDisplayDeviceConfig.getHdrBrightnessData();
+
+        assertNotNull(data);
+        assertEquals(2, data.mMaxBrightnessLimits.size());
+        assertEquals(13000, data.mBrightnessDecreaseDebounceMillis);
+        assertEquals(10000, data.mBrightnessDecreaseDurationMillis);
+        assertEquals(1000, data.mBrightnessIncreaseDebounceMillis);
+        assertEquals(11000, data.mBrightnessIncreaseDurationMillis);
+
+        assertEquals(0.3f, data.mMaxBrightnessLimits.get(500f), SMALL_DELTA);
+        assertEquals(0.6f, data.mMaxBrightnessLimits.get(1200f), SMALL_DELTA);
+    }
+
     private void verifyConfigValuesFromConfigResource() {
         assertNull(mDisplayDeviceConfig.getName());
         assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
@@ -694,6 +712,25 @@
                 + "</proxSensor>\n";
     }
 
+    private String getHdrBrightnessConfig() {
+        return "<hdrBrightnessConfig>\n"
+              + "    <brightnessMap>\n"
+              + "        <point>\n"
+              + "            <first>500</first>\n"
+              + "            <second>0.3</second>\n"
+              + "        </point>\n"
+              + "        <point>\n"
+              + "           <first>1200</first>\n"
+              + "           <second>0.6</second>\n"
+              + "        </point>\n"
+              + "    </brightnessMap>\n"
+              + "    <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>\n"
+              + "    <brightnessIncreaseDurationMillis>11000</brightnessIncreaseDurationMillis>\n"
+              + "    <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>\n"
+              + "    <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>\n"
+              + "</hdrBrightnessConfig>";
+    }
+
     private String getContent() {
         return getContent(getValidLuxThrottling(), getValidProxSensor());
     }
@@ -784,6 +821,7 @@
                 +            "</point>\n"
                 +       "</sdrHdrRatioMap>\n"
                 +   "</highBrightnessMode>\n"
+                + getHdrBrightnessConfig()
                 + brightnessCapConfig
                 +   "<lightSensor>\n"
                 +       "<type>test_light_sensor</type>\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 e7dc48e..d6b2c87 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -43,6 +43,7 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
@@ -67,7 +68,9 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
 import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.clamper.HdrClamper;
 import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.policy.WindowManagerPolicy;
@@ -97,6 +100,7 @@
     private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
     private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
     private static final float PROX_SENSOR_MAX_RANGE = 5;
+    private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
     private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
     private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
     private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -331,9 +335,9 @@
         when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness);
         listener.onBrightnessChanged(leadBrightness);
         advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat());
+        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false));
         verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                anyFloat());
+                anyFloat(), eq(false));
 
         clearInvocations(mHolder.animator, followerDpc.animator);
 
@@ -347,9 +351,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -379,9 +383,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -409,9 +413,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -441,9 +445,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -480,11 +484,11 @@
         advanceTime(1); // Run updatePowerState
 
         verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow
         verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux);
         verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
         when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
@@ -511,10 +515,10 @@
 
         // The second time, the animation rate should be slow
         verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
         verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux);
         verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
     }
 
     @Test
@@ -548,7 +552,7 @@
         followerListener.onBrightnessChanged(initialFollowerBrightness);
         advanceTime(1);
         verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(followerDpc.displayPowerState.getScreenBrightness())
                 .thenReturn(initialFollowerBrightness);
@@ -569,11 +573,11 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
         when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -584,7 +588,7 @@
         mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc);
         advanceTime(1);
         verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
 
         when(followerDpc.displayPowerState.getScreenBrightness())
                 .thenReturn(initialFollowerBrightness);
@@ -602,10 +606,11 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
-        verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat());
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+        verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(),
+                anyBoolean());
         verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -648,9 +653,9 @@
         secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
         advanceTime(1);
         verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(followerHolder.displayPowerState.getScreenBrightness())
                 .thenReturn(initialFollowerBrightness);
@@ -673,11 +678,11 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
         when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -688,13 +693,87 @@
         mHolder.dpc.stop();
         advanceTime(1);
         verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
         verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
         clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
     }
 
     @Test
+    public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        final float sdrBrightness = 0.1f;
+        final float hdrBrightness = 0.3f;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+        clearInvocations(mHolder.animator);
+
+        mHolder.dpc.updateBrightness();
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+    }
+
+    @Test
+    public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        final float sdrBrightness = 0.1f;
+        final float hdrBrightness = 0.3f;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+        clearInvocations(mHolder.animator);
+
+        mHolder.dpc.updateBrightness();
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+    }
+
+    @Test
     public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
         // We should still set screen state for the default display
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -745,7 +824,7 @@
 
         verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
                 .getAutomaticScreenBrightness();
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
     }
 
     @Test
@@ -783,7 +862,7 @@
 
         verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
                 .getAutomaticScreenBrightness();
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
     }
 
     @Test
@@ -1026,7 +1105,8 @@
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
         // One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged
-        verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat());
+        verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(),
+                eq(false));
     }
 
     @Test
@@ -1135,7 +1215,7 @@
         advanceTime(1); // Run updatePowerState
 
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
         brightness = 0.05f;
@@ -1147,7 +1227,7 @@
 
         // The second time, the animation rate should be slow
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false));
 
         brightness = 0.9f;
         when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
@@ -1157,7 +1237,7 @@
         advanceTime(1); // Run updatePowerState
         // The third time, the animation rate should be slow
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false));
     }
 
     @Test
@@ -1175,6 +1255,28 @@
         verify(mHolder.displayPowerState, times(1)).stop();
     }
 
+    @Test
+    public void testRampRateForHdrContent() {
+        float clampedBrightness = 0.6f;
+        float transitionRate = 35.5f;
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
+        when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(),
+                eq(transitionRate), eq(true));
+    }
+
     /**
      * Creates a mock and registers it to {@link LocalServices}.
      */
@@ -1270,12 +1372,15 @@
         final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
                 mock(ScreenOffBrightnessSensorController.class);
         final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
+        final HdrClamper hdrClamper = mock(HdrClamper.class);
+        final DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
 
         when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
 
         TestInjector injector = spy(new TestInjector(displayPowerState, animator,
                 automaticBrightnessController, wakelockController, brightnessMappingStrategy,
-                hysteresisLevels, screenOffBrightnessSensorController, hbmController));
+                hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
+                flags));
 
         final LogicalDisplay display = mock(LogicalDisplay.class);
         final DisplayDevice device = mock(DisplayDevice.class);
@@ -1289,11 +1394,11 @@
                 mContext, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
-                hbmMetadata, /* bootCompleted= */ false);
+                hbmMetadata, /* bootCompleted= */ false, flags);
 
         return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
                 animator, automaticBrightnessController, wakelockController,
-                screenOffBrightnessSensorController, hbmController, hbmMetadata,
+                screenOffBrightnessSensorController, hbmController, hdrClamper, hbmMetadata,
                 brightnessMappingStrategy, injector);
     }
 
@@ -1311,6 +1416,8 @@
         public final WakelockController wakelockController;
         public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
         public final HighBrightnessModeController hbmController;
+
+        public final HdrClamper hdrClamper;
         public final HighBrightnessModeMetadata hbmMetadata;
         public final BrightnessMappingStrategy brightnessMappingStrategy;
         public final DisplayPowerController2.Injector injector;
@@ -1322,6 +1429,7 @@
                 WakelockController wakelockController,
                 ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
                 HighBrightnessModeController hbmController,
+                HdrClamper hdrClamper,
                 HighBrightnessModeMetadata hbmMetadata,
                 BrightnessMappingStrategy brightnessMappingStrategy,
                 DisplayPowerController2.Injector injector) {
@@ -1334,6 +1442,7 @@
             this.wakelockController = wakelockController;
             this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
             this.hbmController = hbmController;
+            this.hdrClamper = hdrClamper;
             this.hbmMetadata = hbmMetadata;
             this.brightnessMappingStrategy = brightnessMappingStrategy;
             this.injector = injector;
@@ -1350,13 +1459,19 @@
         private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
         private final HighBrightnessModeController mHighBrightnessModeController;
 
+        private final HdrClamper mHdrClamper;
+
+        private final DisplayManagerFlags mFlags;
+
         TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
                 AutomaticBrightnessController automaticBrightnessController,
                 WakelockController wakelockController,
                 BrightnessMappingStrategy brightnessMappingStrategy,
                 HysteresisLevels hysteresisLevels,
                 ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
-                HighBrightnessModeController highBrightnessModeController) {
+                HighBrightnessModeController highBrightnessModeController,
+                HdrClamper hdrClamper,
+                DisplayManagerFlags flags) {
             mDisplayPowerState = dps;
             mAnimator = animator;
             mAutomaticBrightnessController = automaticBrightnessController;
@@ -1365,6 +1480,8 @@
             mHysteresisLevels = hysteresisLevels;
             mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
             mHighBrightnessModeController = highBrightnessModeController;
+            mHdrClamper = hdrClamper;
+            mFlags = flags;
         }
 
         @Override
@@ -1471,6 +1588,15 @@
         }
 
         @Override
+        BrightnessRangeController getBrightnessRangeController(
+                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
+                DisplayDeviceConfig displayDeviceConfig, Handler handler,
+                DisplayManagerFlags flags) {
+            return new BrightnessRangeController(hbmController, modeChangeCallback,
+                    displayDeviceConfig, mHdrClamper, mFlags);
+        }
+
+        @Override
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
                 SensorManager sensorManager, Resources resources) {
             return mDisplayWhiteBalanceControllerMock;
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 2640390..c53e763 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -43,6 +43,7 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
@@ -69,6 +70,7 @@
 import com.android.server.display.RampAnimator.DualRampAnimator;
 import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.policy.WindowManagerPolicy;
@@ -97,6 +99,7 @@
     private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
     private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
     private static final float PROX_SENSOR_MAX_RANGE = 5;
+    private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
     private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
     private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
     private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
@@ -333,9 +336,9 @@
         listener.onBrightnessChanged(leadBrightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
@@ -351,9 +354,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -384,9 +387,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -415,9 +418,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -448,9 +451,9 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -488,11 +491,11 @@
         advanceTime(1); // Run updatePowerState
 
         verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow
         verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux);
         verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
         when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
@@ -519,10 +522,10 @@
 
         // The second time, the animation rate should be slow
         verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
         verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux);
         verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
     }
 
     @Test
@@ -557,7 +560,7 @@
         followerListener.onBrightnessChanged(initialFollowerBrightness);
         advanceTime(1);
         verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(followerDpc.displayPowerState.getScreenBrightness())
                 .thenReturn(initialFollowerBrightness);
@@ -578,11 +581,11 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
         when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -593,7 +596,7 @@
         mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc);
         advanceTime(1);
         verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
 
         when(followerDpc.displayPowerState.getScreenBrightness())
                 .thenReturn(initialFollowerBrightness);
@@ -611,10 +614,11 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
-        verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat());
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+        verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(),
+                anyBoolean());
         verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
@@ -658,9 +662,9 @@
         secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
         advanceTime(1);
         verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(followerHolder.displayPowerState.getScreenBrightness())
                 .thenReturn(initialFollowerBrightness);
@@ -683,11 +687,11 @@
         listener.onBrightnessChanged(brightness);
         advanceTime(1); // Send messages, run updatePowerState
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
         verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
         when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
@@ -698,9 +702,9 @@
         mHolder.dpc.stop();
         advanceTime(1);
         verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
         verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
         clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
     }
 
@@ -753,19 +757,21 @@
 
         verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
                 .getAutomaticScreenBrightness();
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
     }
 
     @Test
     public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+
         Settings.System.putInt(mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
@@ -788,7 +794,7 @@
 
         verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
                 .getAutomaticScreenBrightness();
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
     }
 
     @Test
@@ -1033,7 +1039,8 @@
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
         // One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged
-        verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat());
+        verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(),
+                eq(false));
     }
 
     @Test
@@ -1142,7 +1149,7 @@
         advanceTime(1); // Run updatePowerState
 
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE));
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
 
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
         brightness = 0.05f;
@@ -1154,7 +1161,7 @@
 
         // The second time, the animation rate should be slow
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false));
 
         brightness = 0.9f;
         when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
@@ -1164,7 +1171,7 @@
         advanceTime(1); // Run updatePowerState
         // The third time, the animation rate should be slow
         verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE));
+                eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false));
     }
 
     @Test
@@ -1182,6 +1189,77 @@
         verify(mHolder.displayPowerState, times(1)).stop();
     }
 
+    @Test
+    public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        final float sdrBrightness = 0.1f;
+        final float hdrBrightness = 0.3f;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+        clearInvocations(mHolder.animator);
+
+        mHolder.dpc.updateBrightness();
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+    }
+
+    @Test
+    public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        final float sdrBrightness = 0.1f;
+        final float hdrBrightness = 0.3f;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+        clearInvocations(mHolder.animator);
+
+        mHolder.dpc.updateBrightness();
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+    }
+
     private void advanceTime(long timeMs) {
         mClock.fastForward(timeMs);
         mTestLooper.dispatchAll();
@@ -1280,6 +1358,7 @@
         final HighBrightnessModeMetadata hbmMetadata = mock(HighBrightnessModeMetadata.class);
         final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class);
         final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
+        final DisplayManagerFlags flags = mock(DisplayManagerFlags.class);
 
         setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
 
@@ -1287,7 +1366,7 @@
                 mContext, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
-                hbmMetadata, /* bootCompleted= */ false);
+                hbmMetadata, /* bootCompleted= */ false, flags);
 
         return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
                 animator, automaticBrightnessController, screenOffBrightnessSensorController,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
new file mode 100644
index 0000000..810a8b2
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.FloatProperty;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class RampAnimatorTest {
+
+    private static final float FLOAT_TOLERANCE = 0.0000001f;
+
+    private RampAnimator<TestObject> mRampAnimator;
+
+    private final TestObject mTestObject = new TestObject();
+
+    private final FloatProperty<TestObject> mTestProperty = new FloatProperty<>("mValue") {
+        @Override
+        public void setValue(TestObject object, float value) {
+            object.mValue = value;
+        }
+
+        @Override
+        public Float get(TestObject object) {
+            return object.mValue;
+        }
+    };
+
+    @Before
+    public void setUp() {
+        mRampAnimator = new RampAnimator<>(mTestObject, mTestProperty, () -> 0);
+    }
+
+    @Test
+    public void testInitialValueUsedInLastAnimationStep() {
+        mRampAnimator.setAnimationTarget(0.67f, 0.1f, false);
+
+        assertEquals(0.67f, mTestObject.mValue, 0);
+    }
+
+    @Test
+    public void testAnimationStep_respectTimeLimits() {
+        // animation is limited to 2s
+        mRampAnimator.setAnimationTimeLimits(2_000, 2_000);
+        // initial brightness value, applied immediately, in HLG = 0.8716434
+        mRampAnimator.setAnimationTarget(0.5f, 0.1f, false);
+        // expected brightness, in HLG = 0.9057269
+        // delta = 0.0340835, duration = 3.40835s > 2s
+        // new rate = delta/2 = 0.01704175 u/s
+        mRampAnimator.setAnimationTarget(0.6f, 0.01f, false);
+        // animation step = 1s, new HGL = 0.88868515
+        mRampAnimator.performNextAnimationStep(1_000_000_000);
+        // converted back to Linear
+        assertEquals(0.54761934f, mTestObject.mValue, FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void testAnimationStep_ignoreTimeLimits() {
+        // animation is limited to 2s
+        mRampAnimator.setAnimationTimeLimits(2_000, 2_000);
+        // initial brightness value, applied immediately, in HLG = 0.8716434
+        mRampAnimator.setAnimationTarget(0.5f, 0.1f, false);
+        // rate = 0.01f, time limits are ignored
+        mRampAnimator.setAnimationTarget(0.6f, 0.01f, true);
+        // animation step = 1s, new HGL = 0.8816434
+        mRampAnimator.performNextAnimationStep(1_000_000_000);
+        // converted back to Linear
+        assertEquals(0.52739114f, mTestObject.mValue, FLOAT_TOLERANCE);
+    }
+
+    private static class TestObject {
+        private float mValue;
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
new file mode 100644
index 0000000..0ebe46a
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.display.brightness.clamper;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.PowerManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+public class HdrClamperTest {
+
+    public static final float FLOAT_TOLERANCE = 0.0001f;
+
+    @Rule
+    public MockitoRule mRule = MockitoJUnit.rule();
+
+    @Mock
+    private BrightnessClamperController.ClamperChangeListener mMockListener;
+
+    OffsettableClock mClock = new OffsettableClock.Stopped();
+
+    private final TestHandler mTestHandler = new TestHandler(null, mClock);
+
+
+    private HdrClamper mHdrClamper;
+
+
+    @Before
+    public void setUp() {
+        mHdrClamper = new HdrClamper(mMockListener, mTestHandler);
+        configureClamper();
+    }
+
+    @Test
+    public void testClamper_AmbientLuxChangesAboveLimit() {
+        mHdrClamper.onAmbientLuxChange(500);
+
+        assertFalse(mTestHandler.hasMessagesOrCallbacks());
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void testClamper_AmbientLuxChangesBelowLimit() {
+        mHdrClamper.onAmbientLuxChange(499);
+
+        assertTrue(mTestHandler.hasMessagesOrCallbacks());
+        TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+        assertEquals(2000, msgInfo.sendTime);
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+
+        mClock.fastForward(2000);
+        mTestHandler.timeAdvance();
+        assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void testClamper_AmbientLuxChangesBelowLimit_ThenFastAboveLimit() {
+        mHdrClamper.onAmbientLuxChange(499);
+        mHdrClamper.onAmbientLuxChange(500);
+
+        assertFalse(mTestHandler.hasMessagesOrCallbacks());
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void testClamper_AmbientLuxChangesBelowLimit_ThenSlowlyAboveLimit() {
+        mHdrClamper.onAmbientLuxChange(499);
+        mClock.fastForward(2000);
+        mTestHandler.timeAdvance();
+
+        mHdrClamper.onAmbientLuxChange(500);
+
+        assertTrue(mTestHandler.hasMessagesOrCallbacks());
+        TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+        assertEquals(3000, msgInfo.sendTime); // 2000 + 1000
+
+        mClock.fastForward(1000);
+        mTestHandler.timeAdvance();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+    }
+
+    private void configureClamper() {
+        mHdrClamper.getConfiguration().mMaxBrightnessLimits.put(500f, 0.6f);
+        mHdrClamper.getConfiguration().mIncreaseConfig.mDebounceTimeMillis = 1000;
+        mHdrClamper.getConfiguration().mIncreaseConfig.mTransitionTimeMillis = 1500;
+        mHdrClamper.getConfiguration().mDecreaseConfig.mDebounceTimeMillis = 2000;
+        mHdrClamper.getConfiguration().mDecreaseConfig.mTransitionTimeMillis = 2500;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 525bfd7..5b1508b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -16,7 +16,6 @@
 package com.android.server;
 
 import static androidx.test.InstrumentationRegistry.getContext;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -42,7 +41,6 @@
 import static com.android.server.DeviceIdleController.STATE_SENSING;
 import static com.android.server.DeviceIdleController.lightStateToString;
 import static com.android.server.DeviceIdleController.stateToString;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -2418,7 +2416,7 @@
     }
 
     @Test
-    public void testModeManager_NoModeManagerLocalService_AddListenerNotCalled() {
+    public void testModeManager_NoModeManagerLocalService_AddQuickDozeListenerNotCalled() {
         mConstants.USE_MODE_MANAGER = true;
         doReturn(null)
                 .when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -2430,6 +2428,33 @@
     }
 
     @Test
+    public void testModeManager_NoModeManagerLocalService_AddOffBodyListenerNotCalled() {
+        mConstants.USE_MODE_MANAGER = true;
+        doReturn(null)
+                .when(() -> LocalServices.getService(WearModeManagerInternal.class));
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+        verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+                eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(),
+                eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer));
+    }
+
+    @Test
+    public void testModeManager_USEMODEMANAGERIsFalse_AddListenerNotCalled() {
+        mConstants.USE_MODE_MANAGER = false;
+        doReturn(new Object())
+                .when(() -> LocalServices.getService(WearModeManagerInternal.class));
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+        verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+                eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(),
+                eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer));
+        verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
+                eq(WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER), any(),
+                eq(mDeviceIdleController.mModeManagerQuickDozeRequestConsumer));
+    }
+
+    @Test
     public void testModeManager_NoBatterySaver_QuickDoze() {
         mConstants.USE_MODE_MANAGER = true;
         PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
@@ -2469,6 +2494,102 @@
         assertTrue(mDeviceIdleController.isQuickDozeEnabled());
     }
 
+    @Test
+    public void testModeManager_QuickDozeRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() {
+        mConstants.USE_MODE_MANAGER = true;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+                true).build();
+        when(mPowerManagerInternal.getLowPowerState(anyInt()))
+                .thenReturn(powerSaveState);
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+
+        mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
+    @Test
+    public void testModeManager_QuickDozeRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() {
+        mConstants.USE_MODE_MANAGER = true;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+                true).build();
+        when(mPowerManagerInternal.getLowPowerState(anyInt()))
+                .thenReturn(powerSaveState);
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+
+        mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
+    @Test
+    public void testModeManager_QuickDozeRequestedBatterySaverDisabledOnBody_QuickDozeEnabled() {
+        mConstants.USE_MODE_MANAGER = true;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+                false).build();
+        when(mPowerManagerInternal.getLowPowerState(anyInt()))
+                .thenReturn(powerSaveState);
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+
+        mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
+    @Test
+    public void testModeManager_QuickDozeRequestedBatterySaverDisabledOffBody_QuickDozeEnabled() {
+        mConstants.USE_MODE_MANAGER = true;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+                false).build();
+        when(mPowerManagerInternal.getLowPowerState(anyInt()))
+                .thenReturn(powerSaveState);
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+
+        mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
+
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
+    @Test
+    public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() {
+        mConstants.USE_MODE_MANAGER = true;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+                true).build();
+        when(mPowerManagerInternal.getLowPowerState(anyInt()))
+                .thenReturn(powerSaveState);
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+
+        mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false);
+        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
+
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
+    @Test
+    public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() {
+        mConstants.USE_MODE_MANAGER = true;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
+                true).build();
+        when(mPowerManagerInternal.getLowPowerState(anyInt()))
+                .thenReturn(powerSaveState);
+        cleanupDeviceIdleController();
+        setupDeviceIdleController();
+
+        mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true);
+        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
+
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
     private void enterDeepState(int state) {
         switch (state) {
             case STATE_ACTIVE:
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index d988063..b97fd4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -17,8 +17,11 @@
 package com.android.server.pm;
 
 import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -157,11 +160,12 @@
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getResourcesForApplication(eq(PACKAGE))).thenReturn(
                 mock(Resources.class));
-        when(mIcon.compress(eq(Bitmap.CompressFormat.PNG), eq(100), any())).thenReturn(true);
 
         mArchiveManager = spy(new PackageArchiver(mContext, pm));
         doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
-                any(LauncherActivityInfo.class), eq(mUserId));
+                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
+        doReturn(mIcon).when(mArchiveManager).decodeIcon(
+                any(ArchiveState.ArchiveActivityInfo.class));
     }
 
     @Test
@@ -249,7 +253,7 @@
     public void archiveApp_storeIconFails() throws IntentSender.SendIntentException, IOException {
         IOException e = new IOException("IO");
         doThrow(e).when(mArchiveManager).storeIcon(eq(PACKAGE),
-                any(LauncherActivityInfo.class), eq(mUserId));
+                any(LauncherActivityInfo.class), eq(mUserId), anyInt());
 
         mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
         rule.mocks().getHandler().flush();
@@ -272,7 +276,7 @@
 
         verify(mInstallerService).uninstall(
                 eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)),
-                eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender),
+                eq(CALLER_PACKAGE), eq(DELETE_ARCHIVE | DELETE_KEEP_DATA), eq(mIntentSender),
                 eq(UserHandle.CURRENT.getIdentifier()), anyInt());
         assertThat(mPackageSetting.readUserState(
                 UserHandle.CURRENT.getIdentifier()).getArchiveState()).isEqualTo(
@@ -372,6 +376,38 @@
         assertThat(intent.getPackage()).isEqualTo(INSTALLER_PACKAGE);
     }
 
+    @Test
+    public void getArchivedAppIcon_packageNotInstalled() {
+        when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
+                null);
+
+        Exception e = assertThrows(
+                ParcelableException.class,
+                () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
+        assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+        assertThat(e.getCause()).hasMessageThat().isEqualTo(
+                String.format("Package %s not found.", PACKAGE));
+    }
+
+    @Test
+    public void getArchivedAppIcon_notArchived() {
+        Exception e = assertThrows(
+                ParcelableException.class,
+                () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
+        assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+        assertThat(e.getCause()).hasMessageThat().isEqualTo(
+                String.format("Package %s is not currently archived.", PACKAGE));
+    }
+
+    @Test
+    public void getArchivedAppIcon_success() {
+        mUserState.setArchiveState(createArchiveState()).setInstalled(false);
+
+        assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isEqualTo(
+                mIcon);
+    }
+
+
     private static ArchiveState createArchiveState() {
         List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
         for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
new file mode 100644
index 0000000..9fdbdda
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.am;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.Presubmit;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:AnrTimerTest
+ */
+@SmallTest
+@Presubmit
+public class AnrTimerTest {
+
+    /**
+     * A handler that allows control over when to dispatch messages and callbacks. Because most
+     * Handler methods are final, the only thing this handler can intercept is sending messages.
+     * This handler allows unit tests to be written without a need to sleep (which leads to flaky
+     * tests).
+     *
+     * This code was cloned from {@link com.android.systemui.utils.os.FakeHandler}.
+     */
+    static class TestHandler extends Handler {
+
+        private boolean mImmediate = true;
+        private ArrayList<Message> mQueuedMessages = new ArrayList<>();
+
+        ArrayList<Long> mDelays = new ArrayList<>();
+
+        TestHandler(Looper looper, Callback callback, boolean immediate) {
+            super(looper, callback);
+            mImmediate = immediate;
+        }
+
+        TestHandler(Looper looper, Callback callback) {
+            this(looper, callback, true);
+        }
+
+        /**
+         * Override sendMessageAtTime.  In immediate mode, the message is immediately dispatched.
+         * In non-immediate mode, the message is enqueued to the real handler.  In both cases, the
+         * original delay is computed by comparing the target dispatch time with 'now'.  This
+         * computation is prone to errors if the code experiences delays.  The computed time is
+         * captured in the mDelays list.
+         */
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            long delay = uptimeMillis - SystemClock.uptimeMillis();
+            mDelays.add(delay);
+            if (mImmediate) {
+                mQueuedMessages.add(msg);
+                dispatchQueuedMessages();
+            } else {
+                super.sendMessageAtTime(msg, uptimeMillis);
+            }
+            return true;
+        }
+
+        void setImmediate(boolean immediate) {
+            mImmediate = immediate;
+        }
+
+        /** Dispatch any messages that have been queued on the calling thread. */
+        void dispatchQueuedMessages() {
+            ArrayList<Message> messages = new ArrayList<>(mQueuedMessages);
+            mQueuedMessages.clear();
+            for (Message msg : messages) {
+                dispatchMessage(msg);
+            }
+        }
+
+        /**
+         * Compare the captured delays with the input array.  The comparison is fuzzy because the
+         * captured delay (see sendMessageAtTime) is affected by process delays.
+         */
+        void verifyDelays(long[] r) {
+            final long FUZZ = 10;
+            assertEquals(r.length, mDelays.size());
+            for (int i = 0; i < mDelays.size(); i++) {
+                long t = r[i];
+                long v = mDelays.get(i);
+                assertTrue(v >= t - FUZZ && v <= t + FUZZ);
+            }
+        }
+    }
+
+    private Handler mHandler;
+    private CountDownLatch mLatch = null;
+    private ArrayList<Message> mMessages;
+
+    // The commonly used message timeout key.
+    private static final int MSG_TIMEOUT = 1;
+
+    @Before
+    public void setUp() {
+        mHandler = new Handler(Looper.getMainLooper(), this::expirationHandler);
+        mMessages = new ArrayList<>();
+        mLatch = new CountDownLatch(1);
+        AnrTimer.resetTimerListForHermeticTest();
+    }
+
+    @After
+    public void tearDown() {
+        mHandler = null;
+        mMessages = null;
+    }
+
+    // When a timer expires, set the expiration time in the message and add it to the queue.
+    private boolean expirationHandler(Message msg) {
+        mMessages.add(Message.obtain(msg));
+        mLatch.countDown();
+        return false;
+    }
+
+    // The test argument includes a pid and uid, and a tag.  The tag is used to distinguish
+    // different message instances.
+    private static class TestArg {
+        final int pid;
+        final int uid;
+        final int tag;
+
+        TestArg(int pid, int uid, int tag) {
+            this.pid = pid;
+            this.uid = uid;
+            this.tag = tag;
+        }
+        @Override
+        public String toString() {
+            return String.format("pid=%d uid=%d tag=%d", pid, uid, tag);
+        }
+    }
+
+    /**
+     * An instrumented AnrTimer.
+     */
+    private class TestAnrTimer extends AnrTimer {
+        // A local copy of 'what'.  The field in AnrTimer is private.
+        final int mWhat;
+
+        TestAnrTimer(Handler h, int key, String tag) {
+            super(h, key, tag);
+            mWhat = key;
+        }
+
+        TestAnrTimer() {
+            this(mHandler, MSG_TIMEOUT, caller());
+        }
+
+        TestAnrTimer(Handler h, int key, String tag, boolean extend, TestInjector injector) {
+            super(h, key, tag, extend, injector);
+            mWhat = key;
+        }
+
+        TestAnrTimer(boolean extend, TestInjector injector) {
+            this(mHandler, MSG_TIMEOUT, caller(), extend, injector);
+        }
+
+        // Return the name of method that called the constructor, assuming that this function is
+        // called from inside the constructor.  The calling method is used to name the AnrTimer
+        // instance so that logs are easier to understand.
+        private static String caller() {
+            final int n = 4;
+            StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+            if (stack.length < n+1) return "test";
+            return stack[n].getMethodName();
+        }
+
+        boolean start(TestArg arg, long millis) {
+            return start(arg, arg.pid, arg.uid, millis);
+        }
+
+        int what() {
+            return mWhat;
+        }
+    }
+
+    private static class TestTracker extends AnrTimer.CpuTracker {
+        long index = 0;
+        final int skip;
+        TestTracker(int skip) {
+            this.skip = skip;
+        }
+        long delay(int pid) {
+            return index++ * skip;
+        }
+    }
+
+    private class TestInjector extends AnrTimer.Injector {
+        final boolean mImmediate;
+        final AnrTimer.CpuTracker mTracker;
+        TestHandler mTestHandler;
+
+        TestInjector(int skip, boolean immediate) {
+            mTracker = new TestTracker(skip);
+            mImmediate = immediate;
+        }
+
+        TestInjector(int skip) {
+            this(skip, true);
+        }
+
+        @Override
+        Handler getHandler(Handler.Callback callback) {
+            if (mTestHandler == null) {
+                mTestHandler = new TestHandler(mHandler.getLooper(), callback, mImmediate);
+            }
+            return mTestHandler;
+        }
+
+        /** Fetch the allocated handle. This does not check for nulls. */
+        TestHandler getHandler() {
+            return mTestHandler;
+        }
+
+        AnrTimer.CpuTracker getTracker() {
+            return mTracker;
+        }
+    }
+
+    // Tests
+    // 1. Start a timer and wait for expiration.
+    // 2. Start a timer and cancel it.  Verify no expiration.
+    // 3. Start a timer.  Shortly thereafter, restart it.  Verify only one expiration.
+    // 4. Start a couple of timers.  Verify max active timers.  Discard one and verify the active
+    //    count drops by 1.  Accept one and verify the active count drops by 1.
+
+
+    @Test
+    public void testSimpleTimeout() throws Exception {
+        // Create an immediate TestHandler.
+        TestInjector injector = new TestInjector(0);
+        TestAnrTimer timer = new TestAnrTimer(false, injector);
+        TestArg t = new TestArg(1, 1, 3);
+        assertTrue(timer.start(t, 10));
+        // Delivery is immediate but occurs on a different thread.
+        assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS));
+        assertEquals(1, mMessages.size());
+        Message m = mMessages.get(0);
+        assertEquals(timer.what(), m.what);
+        assertEquals(t, m.obj);
+
+        // Verify that the timer is still present.
+        assertEquals(1, AnrTimer.sizeOfTimerList());
+        assertTrue(timer.accept(t));
+        assertEquals(0, AnrTimer.sizeOfTimerList());
+
+        // Verify that the timer no longer exists.
+        assertFalse(timer.accept(t));
+    }
+
+    @Test
+    public void testCancel() throws Exception {
+        // Create an non-immediate TestHandler.
+        TestInjector injector = new TestInjector(0, false);
+        TestAnrTimer timer = new TestAnrTimer(false, injector);
+
+        Handler handler = injector.getHandler();
+        assertNotNull(handler);
+        assertTrue(handler instanceof TestHandler);
+
+        // The tests that follow check for a 'what' of 0 (zero), which is the message key used
+        // by AnrTimer internally.
+        TestArg t = new TestArg(1, 1, 3);
+        assertFalse(handler.hasMessages(0));
+        assertTrue(timer.start(t, 100));
+        assertTrue(handler.hasMessages(0));
+        assertTrue(timer.cancel(t));
+        assertFalse(handler.hasMessages(0));
+
+        // Verify that no expiration messages were delivered.
+        assertEquals(0, mMessages.size());
+        assertEquals(0, AnrTimer.sizeOfTimerList());
+    }
+
+    @Test
+    public void testRestart() throws Exception {
+        // Create an non-immediate TestHandler.
+        TestInjector injector = new TestInjector(0, false);
+        TestAnrTimer timer = new TestAnrTimer(false, injector);
+
+        TestArg t = new TestArg(1, 1, 3);
+        assertTrue(timer.start(t, 2500));
+        assertTrue(timer.start(t, 1000));
+
+        // Verify that the test handler saw two timeouts.
+        injector.getHandler().verifyDelays(new long[] { 2500, 1000 });
+
+        // Verify that there is a single timer.  Then cancel it.
+        assertEquals(1, AnrTimer.sizeOfTimerList());
+        assertTrue(timer.cancel(t));
+        assertEquals(0, AnrTimer.sizeOfTimerList());
+    }
+
+    @Test
+    public void testExtendNormal() throws Exception {
+        // Create an immediate TestHandler.
+        TestInjector injector = new TestInjector(5);
+        TestAnrTimer timer = new TestAnrTimer(true, injector);
+        TestArg t = new TestArg(1, 1, 3);
+        assertTrue(timer.start(t, 10));
+
+        assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS));
+        assertEquals(1, mMessages.size());
+        Message m = mMessages.get(0);
+        assertEquals(timer.what(), m.what);
+        assertEquals(t, m.obj);
+
+        // Verify that the test handler saw two timeouts: one of 10ms and one of 5ms.
+        injector.getHandler().verifyDelays(new long[] { 10, 5 });
+
+        // Verify that the timer is still present. Then remove it and verify that the list is
+        // empty.
+        assertEquals(1, AnrTimer.sizeOfTimerList());
+        assertTrue(timer.accept(t));
+        assertEquals(0, AnrTimer.sizeOfTimerList());
+    }
+
+    @Test
+    public void testExtendOversize() throws Exception {
+        // Create an immediate TestHandler.
+        TestInjector injector = new TestInjector(25);
+        TestAnrTimer timer = new TestAnrTimer(true, injector);
+        TestArg t = new TestArg(1, 1, 3);
+        assertTrue(timer.start(t, 10));
+
+        assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS));
+        assertEquals(1, mMessages.size());
+        Message m = mMessages.get(0);
+        assertEquals(timer.what(), m.what);
+        assertEquals(t, m.obj);
+
+        // Verify that the test handler saw two timeouts: one of 10ms and one of 10ms.
+        injector.getHandler().verifyDelays(new long[] { 10, 10 });
+
+        // Verify that the timer is still present. Then remove it and verify that the list is
+        // empty.
+        assertEquals(1, AnrTimer.sizeOfTimerList());
+        assertTrue(timer.accept(t));
+        assertEquals(0, AnrTimer.sizeOfTimerList());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java b/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
index f5005fd..38bb3de 100644
--- a/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
@@ -152,6 +152,50 @@
     }
 
     @Test
+    public void testApiStartStopFgs() throws InterruptedException {
+        ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
+        record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+        mFgsLogger.logForegroundServiceApiEventBegin(FOREGROUND_SERVICE_API_TYPE_CAMERA,
+                1, 1, "aPackageHasNoName");
+        Thread.sleep(2000);
+
+        resetAndVerifyZeroInteractions();
+
+        mFgsLogger.logForegroundServiceApiEventEnd(FOREGROUND_SERVICE_API_TYPE_CAMERA, 1, 1);
+
+        resetAndVerifyZeroInteractions();
+
+        mFgsLogger.logForegroundServiceStart(1, 1, record);
+
+        resetAndVerifyZeroInteractions();
+    }
+
+    @Test
+    public void testFgsStartStopApiStartStop() throws InterruptedException {
+        ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
+        record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+        mFgsLogger.logForegroundServiceStart(1, 1, record);
+
+        resetAndVerifyZeroInteractions();
+
+        mFgsLogger.logForegroundServiceStop(1, record);
+
+        resetAndVerifyZeroInteractions();
+
+        mFgsLogger.logForegroundServiceApiEventBegin(FOREGROUND_SERVICE_API_TYPE_CAMERA,
+                1, 1, "aPackageHasNoName");
+        Thread.sleep(2000);
+
+        resetAndVerifyZeroInteractions();
+
+        mFgsLogger.logForegroundServiceApiEventEnd(FOREGROUND_SERVICE_API_TYPE_CAMERA, 1, 1);
+
+        resetAndVerifyZeroInteractions();
+    }
+
+    @Test
     public void testMultipleStartStopApis() throws InterruptedException {
         ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
         record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
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 a11a8f5..d2e83e9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -87,6 +87,8 @@
     public void setUp() {
 
         when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getBoolean(eq(R.bool.config_biometricFrrNotificationEnabled)))
+                .thenReturn(true);
         when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold),
                 anyInt(), anyInt())).thenReturn(FRR_THRESHOLD);
 
@@ -110,7 +112,6 @@
                 0 /* modality */, mBiometricNotification);
     }
 
-
     @Test
     public void authenticate_authenticationSucceeded_mapShouldBeUpdated() {
         // Assert that the user doesn't exist in the map initially.
@@ -342,4 +343,32 @@
         // Assert that notification count has been updated.
         assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
     }
+
+    @Test
+    public void authenticate_featureDisabled_mapMustNotBeUpdated() {
+        // Disable the feature.
+        when(mResources.getBoolean(eq(R.bool.config_biometricFrrNotificationEnabled)))
+                .thenReturn(false);
+        AuthenticationStatsCollector authenticationStatsCollector =
+                new AuthenticationStatsCollector(mContext, 0 /* modality */,
+                        mBiometricNotification);
+
+        authenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
+                new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */,
+                        400 /* rejectedAttempts */, 0 /* enrollmentNotifications */,
+                        0 /* modality */));
+
+        authenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
+
+        // Assert that no notification should be sent.
+        verify(mBiometricNotification, never()).sendFaceEnrollNotification(any());
+        verify(mBiometricNotification, never()).sendFpEnrollNotification(any());
+        // Assert that data hasn't been updated.
+        AuthenticationStats authenticationStats = authenticationStatsCollector
+                .getAuthenticationStatsForUser(USER_ID_1);
+        assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
+        assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+        assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
+        assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
new file mode 100644
index 0000000..75d71da
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.media;
+
+import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+
+import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BluetoothRouteControllerTest {
+
+    private final BluetoothRouteController.BluetoothRoutesUpdatedListener
+            mBluetoothRoutesUpdatedListener = routes -> {
+                // Empty on purpose.
+            };
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
+    public void createInstance_audioPoliciesFlagIsDisabled_createsLegacyController() {
+        BluetoothRouteController deviceRouteController =
+                BluetoothRouteController.createInstance(mContext, mBluetoothRoutesUpdatedListener);
+
+        Truth.assertThat(deviceRouteController).isInstanceOf(LegacyBluetoothRouteController.class);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
+    public void createInstance_audioPoliciesFlagIsEnabled_createsAudioPoliciesController() {
+        BluetoothRouteController deviceRouteController =
+                BluetoothRouteController.createInstance(mContext, mBluetoothRoutesUpdatedListener);
+
+        Truth.assertThat(deviceRouteController)
+                .isInstanceOf(AudioPoliciesBluetoothRouteController.class);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
new file mode 100644
index 0000000..ec4b8a8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.media;
+
+import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+
+import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DeviceRouteControllerTest {
+
+    private final DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener =
+            deviceRoute -> {
+                // Empty on purpose.
+            };
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
+    public void createInstance_audioPoliciesFlagIsDisabled_createsLegacyController() {
+        DeviceRouteController deviceRouteController =
+                DeviceRouteController.createInstance(mContext, mOnDeviceRouteChangedListener);
+
+        Truth.assertThat(deviceRouteController).isInstanceOf(LegacyDeviceRouteController.class);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
+    public void createInstance_audioPoliciesFlagIsEnabled_createsAudioPoliciesController() {
+        DeviceRouteController deviceRouteController =
+                DeviceRouteController.createInstance(mContext, mOnDeviceRouteChangedListener);
+
+        Truth.assertThat(deviceRouteController)
+                .isInstanceOf(AudioPoliciesDeviceRouteController.class);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index b0fd606..d85768d 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -79,6 +79,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for the {@link MediaProjectionManagerService} class.
  *
@@ -202,7 +205,8 @@
     }
 
     @Test
-    public void testCreateProjection_priorProjectionGrant() throws NameNotFoundException {
+    public void testCreateProjection_priorProjectionGrant() throws
+            NameNotFoundException, InterruptedException {
         // Create a first projection.
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
         FakeIMediaProjectionCallback callback1 = new FakeIMediaProjectionCallback();
@@ -214,9 +218,13 @@
         FakeIMediaProjectionCallback callback2 = new FakeIMediaProjectionCallback();
         secondProjection.start(callback2);
 
-        // Check that the second projection's callback hasn't been stopped.
-        assertThat(callback1.mStopped).isTrue();
-        assertThat(callback2.mStopped).isFalse();
+        // Check that the first projection get stopped, but not the second projection.
+        final int timeout = 5;
+        boolean stoppedCallback1 = callback1.mLatch.await(timeout, TimeUnit.SECONDS);
+        boolean stoppedCallback2 = callback2.mLatch.await(timeout, TimeUnit.SECONDS);
+
+        assertThat(stoppedCallback1).isTrue();
+        assertThat(stoppedCallback2).isFalse();
     }
 
     @Test
@@ -803,11 +811,10 @@
     }
 
     private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
-        boolean mStopped = false;
-
+        CountDownLatch mLatch = new CountDownLatch(1);
         @Override
         public void onStop() throws RemoteException {
-            mStopped = true;
+            mLatch.countDown();
         }
 
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index b522cab..5147a08 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -28,6 +28,7 @@
 import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_OTHER;
 import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
 import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -48,6 +49,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.time.Duration;
+
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -230,4 +233,12 @@
                 NotificationRecordLogger.NotificationCancelledEvent.fromCancelReason(
                         REASON_CANCEL, DISMISSAL_OTHER));
     }
+
+    @Test
+    public void testGetAgeInMinutes() {
+        long postTimeMs = Duration.ofMinutes(5).toMillis();
+        long whenMs = Duration.ofMinutes(2).toMillis();
+        int age = NotificationRecordLogger.getAgeInMinutes(postTimeMs, whenMs);
+        assertThat(age).isEqualTo(3);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 318f932..3034942 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -248,8 +248,7 @@
         verify(mPermManager).grantRuntimePermission(
                 "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10);
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
-                FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+                USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
     }
 
     @Test
@@ -267,8 +266,7 @@
         verify(mPermManager).grantRuntimePermission(
                 "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10);
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
-                FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+                USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
     }
 
     @Test
@@ -282,8 +280,7 @@
                 eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS),
                 eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString());
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
-                FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
+                USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10);
     }
 
     @Test
@@ -310,8 +307,7 @@
                 eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS),
                 eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString());
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0,
-                true, Context.DEVICE_ID_DEFAULT, 10);
+                USER_FLAG_MASK, 0, true, Context.DEVICE_ID_DEFAULT, 10);
     }
 
     @Test
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index d6a3877..42e3383 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -47,8 +47,6 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/>
-    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
-
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
@@ -106,11 +104,6 @@
             android:showWhenLocked="true"
             android:turnScreenOn="true" />
 
-        <activity android:name="android.app.Activity"
-            android:exported="true"
-            android:showWhenLocked="true"
-            android:turnScreenOn="true" />
-
         <activity
             android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
             android:exported="true">
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 4791946..0a7bb00 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -297,6 +297,7 @@
         mPhoneWindowManager.overrideUserSetupComplete();
         mPhoneWindowManager.setupAssistForLaunch();
         mPhoneWindowManager.overrideTogglePanel();
+        mPhoneWindowManager.overrideInjectKeyEvent();
     }
 
     @Test
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 e301da7..ef28ffa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -77,6 +77,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.Vibrator;
+import android.os.VibratorInfo;
 import android.service.dreams.DreamManagerInternal;
 import android.telecom.TelecomManager;
 import android.util.FeatureFlagUtils;
@@ -138,6 +139,7 @@
     @Mock private TelecomManager mTelecomManager;
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vibrator mVibrator;
+    @Mock private VibratorInfo mVibratorInfo;
     @Mock private PowerManager mPowerManager;
     @Mock private WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncsImpl;
     @Mock private InputMethodManagerInternal mInputMethodManagerInternal;
@@ -246,6 +248,7 @@
         doReturn(mTelecomManager).when(mPhoneWindowManager).getTelecommService();
         doNothing().when(mNotificationManager).silenceNotificationSound();
         doReturn(mNotificationManager).when(mPhoneWindowManager).getNotificationService();
+        doReturn(mVibratorInfo).when(mVibrator).getInfo();
         doReturn(mVibrator).when(mContext).getSystemService(eq(Context.VIBRATOR_SERVICE));
 
         final PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
@@ -447,7 +450,7 @@
     }
 
     void overrideSearchManager(SearchManager searchManager) {
-        mPhoneWindowManager.mSearchManager = searchManager;
+        doReturn(searchManager).when(mContext).getSystemService(eq(SearchManager.class));
     }
 
     void assumeResolveActivityNotNull() {
@@ -464,6 +467,10 @@
         doReturn(device).when(mInputManager).getInputDevice(anyInt());
     }
 
+    void overrideInjectKeyEvent() {
+        doReturn(true).when(mInputManager).injectInputEvent(any(KeyEvent.class), anyInt());
+    }
+
     void overrideSearchKeyBehavior(int behavior) {
         mPhoneWindowManager.mSearchKeyBehavior = behavior;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ae58700..0494dfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -489,7 +489,7 @@
         ensureActivityConfiguration(activity);
 
         verify(mAtm.getLifecycleManager(), never())
-                .scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class));
+                .scheduleTransaction(any(), isA(ActivityConfigurationChangeItem.class));
     }
 
     @Test
@@ -519,7 +519,7 @@
         final ActivityConfigurationChangeItem expected =
                 ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
         verify(mAtm.getLifecycleManager()).scheduleTransaction(
-                eq(activity.app.getThread()), eq(activity.token), eq(expected));
+                eq(activity.app.getThread()), eq(expected));
     }
 
     @Test
@@ -599,7 +599,7 @@
         final ActivityConfigurationChangeItem expected =
                 ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
         verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
-                eq(activity.token), eq(expected));
+                eq(expected));
 
         verify(displayRotation).onSetRequestedOrientation();
     }
@@ -817,7 +817,7 @@
             final ActivityConfigurationChangeItem expected =
                     ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
             verify(mAtm.getLifecycleManager()).scheduleTransaction(
-                    eq(activity.app.getThread()), eq(activity.token), eq(expected));
+                    eq(activity.app.getThread()), eq(expected));
         } finally {
             stack.getDisplayArea().removeChild(stack);
         }
@@ -1787,9 +1787,10 @@
             final ActivityRecord activity = createActivityWithTask();
             final WindowProcessController wpc = activity.app;
             setup.accept(activity);
+            clearInvocations(mAtm.getLifecycleManager());
             activity.getTask().removeImmediately("test");
             try {
-                verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.token),
+                verify(mAtm.getLifecycleManager()).scheduleTransaction(any(),
                         isA(DestroyActivityItem.class));
             } catch (RemoteException ignored) {
             }
@@ -2871,14 +2872,14 @@
                 .setTask(sourceRecord.getTask()).build();
         secondRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
                 true /* startActivity */, sourceRecord);
-        assertFalse(secondRecord.mSplashScreenStyleSolidColor);
+        assertTrue(secondRecord.mAllowIconSplashScreen);
         secondRecord.onStartingWindowDrawn();
 
         final ActivityRecord finalRecord = new ActivityBuilder(mAtm)
                 .setTask(sourceRecord.getTask()).build();
         finalRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
                 true /* startActivity */, secondRecord);
-        assertTrue(finalRecord.mSplashScreenStyleSolidColor);
+        assertFalse(finalRecord.mAllowIconSplashScreen);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 0989db4..568471d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
@@ -26,7 +25,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -36,7 +34,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,13 +41,11 @@
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
@@ -76,7 +71,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 /**
@@ -509,88 +503,4 @@
 
         verify(callback, times(1)).onActivityLaunched(any(), any(), any());
     }
-
-    @Test
-    public void testSandboxServiceInterceptionHappensToIntentWithSandboxActivityAction() {
-        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
-        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
-        PackageManager packageManagerMock = mock(PackageManager.class);
-        String sandboxPackageNameMock = "com.sandbox.mock";
-        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
-        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
-        Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        verify(spyCallback, times(1)).onInterceptActivityLaunch(
-                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
-    }
-
-    @Test
-    public void testSandboxServiceInterceptionHappensToIntentWithSandboxPackage() {
-        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
-        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
-        PackageManager packageManagerMock = mock(PackageManager.class);
-        String sandboxPackageNameMock = "com.sandbox.mock";
-        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
-        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
-        Intent intent = new Intent().setPackage(sandboxPackageNameMock);
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        verify(spyCallback, times(1)).onInterceptActivityLaunch(
-                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
-    }
-
-    @Test
-    public void testSandboxServiceInterceptionHappensToIntentWithComponentNameWithSandboxPackage() {
-        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
-        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
-        PackageManager packageManagerMock = mock(PackageManager.class);
-        String sandboxPackageNameMock = "com.sandbox.mock";
-        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
-        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
-        Intent intent = new Intent().setComponent(new ComponentName(sandboxPackageNameMock, ""));
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        verify(spyCallback, times(1)).onInterceptActivityLaunch(
-                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
-    }
-
-    @Test
-    public void testSandboxServiceInterceptionNotCalledWhenIntentNotRelatedToSandbox() {
-        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
-        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
-
-        PackageManager packageManagerMock = mock(PackageManager.class);
-        String sandboxPackageNameMock = "com.sandbox.mock";
-        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
-        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
-
-        // Intent: null
-        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        // Action: null, Package: null, ComponentName: null
-        Intent intent = new Intent();
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        // Wrong Action
-        intent = new Intent().setAction(Intent.ACTION_VIEW);
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        // Wrong Package
-        intent = new Intent().setPackage("Random");
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        // Wrong ComponentName's package
-        intent = new Intent().setComponent(new ComponentName("Random", ""));
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
-
-        verify(spyCallback, never()).onInterceptActivityLaunch(
-                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 28dd458..a18dbaf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -23,7 +23,6 @@
 
 import android.app.IApplicationThread;
 import android.app.servertransaction.ClientTransaction;
-import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -40,8 +39,7 @@
 
     @Test
     public void testScheduleAndRecycleBinderClientTransaction() throws Exception {
-        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class),
-                new Binder()));
+        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class)));
 
         ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
         clientLifecycleManager.scheduleTransaction(item);
@@ -51,8 +49,7 @@
 
     @Test
     public void testScheduleNoRecycleNonBinderClientTransaction() throws Exception {
-        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class),
-                new Binder()));
+        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class)));
 
         ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
         clientLifecycleManager.scheduleTransaction(item);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index ecd84e1..3d3531e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -304,6 +304,30 @@
                 anyFloat(), anyFloat());
     }
 
+    /**
+     * Test that resizing the output surface results in resizing the mirrored content to fit.
+     */
+    @Test
+    public void testOnConfigurationChanged_resizeSurface() {
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+
+        // Resize the output surface.
+        final Point newSurfaceSize = new Point(Math.round(sSurfaceSize.x / 2f),
+                Math.round(sSurfaceSize.y * 2));
+        doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
+                anyInt());
+        mContentRecorder.onConfigurationChanged(
+                mVirtualDisplayContent.getConfiguration().orientation);
+
+        // No resize is issued, only the initial transformations when we started recording.
+        verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
+                anyFloat());
+        verify(mTransaction, atLeast(2)).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
+                anyFloat(), anyFloat());
+
+    }
+
     @Test
     public void testOnTaskOrientationConfigurationChanged_resizesSurface() {
         mContentRecorder.setContentRecordingSession(mTaskSession);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 1b44c01..2af6745 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -571,8 +571,7 @@
         verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
                 .setIsRefreshAfterRotationRequested(true);
 
-        final ClientTransaction transaction = ClientTransaction.obtain(
-                mActivity.app.getThread(), mActivity.token);
+        final ClientTransaction transaction = ClientTransaction.obtain(mActivity.app.getThread());
         transaction.addCallback(RefreshCallbackItem.obtain(mActivity.token,
                 cycleThroughStop ? ON_STOP : ON_PAUSE));
         transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(mActivity.token,
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index be3f01e..80e169d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -17,15 +17,14 @@
 package com.android.server.wm;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
-import static junit.framework.Assert.assertEquals;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -38,8 +37,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.window.flags.FakeFeatureFlagsImpl;
-
 import org.junit.Before;
 import org.junit.Test;
 
@@ -60,16 +57,12 @@
     private LetterboxConfiguration mLetterboxConfiguration;
     private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
 
-    private MutableFakeFeatureFlagsImpl mMutableFakeFeatureFlags;
-
-
     @Before
     public void setUp() throws Exception {
         mContext = getInstrumentation().getTargetContext();
-        mMutableFakeFeatureFlags = new MutableFakeFeatureFlagsImpl();
         mLetterboxConfigurationPersister = mock(LetterboxConfigurationPersister.class);
         mLetterboxConfiguration = new LetterboxConfiguration(mContext,
-                mLetterboxConfigurationPersister, mMutableFakeFeatureFlags);
+                mLetterboxConfigurationPersister);
     }
 
     @Test
@@ -99,22 +92,6 @@
     }
 
     @Test
-    public void test_whenFlagEnabled_wallpaperIsDefaultBackground() {
-        mMutableFakeFeatureFlags.setLetterboxBackgroundWallpaperFlag(true);
-        assertEquals(LETTERBOX_BACKGROUND_WALLPAPER,
-                mLetterboxConfiguration.getLetterboxBackgroundType());
-        assertEquals(1, mMutableFakeFeatureFlags.getInvocationCount());
-    }
-
-    @Test
-    public void test_whenFlagDisabled_solidColorIsDefaultBackground() {
-        mMutableFakeFeatureFlags.setLetterboxBackgroundWallpaperFlag(false);
-        assertEquals(LETTERBOX_BACKGROUND_SOLID_COLOR,
-                mLetterboxConfiguration.getLetterboxBackgroundType());
-        assertEquals(1, mMutableFakeFeatureFlags.getInvocationCount());
-    }
-
-    @Test
     public void test_whenMovedHorizontally_updatePositionAccordingly() {
         // Starting from center
         assertForHorizontalMove(
@@ -311,23 +288,4 @@
                 false /* forTabletopMode */,
                 LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
     }
-
-    private static class MutableFakeFeatureFlagsImpl extends FakeFeatureFlagsImpl {
-        private boolean mLetterboxBackgroundWallpaperFlag;
-        private int mInvocationCount;
-
-        public void setLetterboxBackgroundWallpaperFlag(boolean letterboxBackgroundWallpaperFlag) {
-            mLetterboxBackgroundWallpaperFlag = letterboxBackgroundWallpaperFlag;
-        }
-
-        @Override
-        public boolean letterboxBackgroundWallpaperFlag() {
-            mInvocationCount++;
-            return mLetterboxBackgroundWallpaperFlag;
-        }
-
-        int getInvocationCount() {
-            return mInvocationCount;
-        }
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 2ad9fa0..5f92fd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -32,6 +32,7 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -811,6 +812,31 @@
                 /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
     }
 
+    @Test
+    public void testOverrideOrientationIfNeeded_userAspectRatioApplied_unspecifiedOverridden() {
+        spyOn(mController);
+        doReturn(true).when(mController).shouldApplyUserMinAspectRatioOverride();
+
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_LOCKED), SCREEN_ORIENTATION_PORTRAIT);
+
+        // unchanged if orientation is specified
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_LANDSCAPE), SCREEN_ORIENTATION_LANDSCAPE);
+    }
+
+    @Test
+    public void testOverrideOrientationIfNeeded_userAspectRatioNotApplied_returnsUnchanged() {
+        spyOn(mController);
+        doReturn(false).when(mController).shouldApplyUserMinAspectRatioOverride();
+
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+    }
+
     // shouldApplyUser...Override
     @Test
     public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
@@ -862,6 +888,39 @@
     }
 
     @Test
+    public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
+            throws Exception {
+        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldEnableUserAspectRatioSettings());
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
+            throws Exception {
+        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldEnableUserAspectRatioSettings());
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientaion_returnsFalse()
+            throws Exception {
+        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldEnableUserAspectRatioSettings());
+    }
+
+    @Test
     public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
             throws Exception {
         prepareActivityThatShouldApplyUserMinAspectRatioOverride();
@@ -898,13 +957,26 @@
         assertTrue(mController.shouldApplyUserMinAspectRatioOverride());
     }
 
-    private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientationreturnsFalse() {
+        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+
+        assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
+    }
+
+    private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
+            boolean orientationRequest) {
         spyOn(mController);
-        doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
+        doReturn(orientationRequest).when(
+                mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
         mDisplayContent.setIgnoreOrientationRequest(true);
         doReturn(USER_MIN_ASPECT_RATIO_3_2).when(mController).getUserMinAspectRatioOverrideCode();
     }
 
+    private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
+    }
+
     private void prepareActivityThatShouldApplyUserFullscreenOverride() {
         spyOn(mController);
         doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index de3a526..491d5b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.PROCESS_STATE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -148,8 +149,9 @@
                 anyInt() /* startFlags */, any() /* profilerInfo */);
 
         // Assume its process is alive because the caller should be the recents service.
-        mSystemServicesTestRule.addProcess(aInfo.packageName, aInfo.processName, 12345 /* pid */,
-                aInfo.applicationInfo.uid);
+        final WindowProcessController proc = mSystemServicesTestRule.addProcess(aInfo.packageName,
+                aInfo.processName, 12345 /* pid */, aInfo.applicationInfo.uid);
+        proc.setCurrentProcState(PROCESS_STATE_HOME);
 
         Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
         // Null animation indicates to preload.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 1dd71e0..fb27d63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -571,26 +571,28 @@
         final Task task = rootTask.getBottomMostTask();
         final ActivityRecord root = task.getTopNonFinishingActivity();
         spyOn(mWm.mLetterboxConfiguration);
-
-        // When device config flag is disabled the button is not enabled
-        doReturn(false).when(mWm.mLetterboxConfiguration)
-                .isUserAppAspectRatioSettingsEnabled();
-        doReturn(false).when(mWm.mLetterboxConfiguration)
-                .isTranslucentLetterboxingEnabled();
-        assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
-
-        // The flag is enabled
-        doReturn(true).when(mWm.mLetterboxConfiguration)
-                .isUserAppAspectRatioSettingsEnabled();
         spyOn(root);
-        doReturn(task).when(root).getOrganizedTask();
-        // When the flag is enabled and the top activity is not in size compat mode.
+        spyOn(root.mLetterboxUiController);
+
+        doReturn(true).when(root.mLetterboxUiController)
+                .shouldEnableUserAspectRatioSettings();
         doReturn(false).when(root).inSizeCompatMode();
+        doReturn(task).when(root).getOrganizedTask();
+
+        // The button should be eligible to be displayed
         assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
 
+        // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled
+        doReturn(false).when(root.mLetterboxUiController)
+                .shouldEnableUserAspectRatioSettings();
+        assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+        doReturn(true).when(root.mLetterboxUiController)
+                .shouldEnableUserAspectRatioSettings();
+
         // When in size compat mode the button is not enabled
         doReturn(true).when(root).inSizeCompatMode();
         assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+        doReturn(false).when(root).inSizeCompatMode();
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
deleted file mode 100644
index e8a847c..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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.server.wm;
-
-import static android.view.InputWindowHandle.USE_SURFACE_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.server.wm.BuildUtils;
-import android.server.wm.CtsWindowInfoUtils;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestName;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@Presubmit
-public class TrustedOverlayTests {
-    private static final String TAG = "TrustedOverlayTests";
-    private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
-
-    @Rule
-    public TestName mName = new TestName();
-
-    private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(
-            Activity.class);
-
-    private Instrumentation mInstrumentation;
-    private Activity mActivity;
-
-    @Before
-    public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mActivity = mActivityRule.launchActivity(null);
-    }
-
-    @Test
-    public void setTrustedOverlayInputWindow() throws InterruptedException {
-        assumeFalse(USE_SURFACE_TRUSTED_OVERLAY);
-        testTrustedOverlayChildHelper(false);
-    }
-
-    @Test
-    public void setTrustedOverlayChildLayer() throws InterruptedException {
-        assumeTrue(USE_SURFACE_TRUSTED_OVERLAY);
-        testTrustedOverlayChildHelper(true);
-    }
-
-    private void testTrustedOverlayChildHelper(boolean expectTrusted) throws InterruptedException {
-        IBinder[] tokens = new IBinder[2];
-        CountDownLatch hostTokenReady = new CountDownLatch(1);
-        mInstrumentation.runOnMainSync(() -> {
-            mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
-            View rootView = mActivity.getWindow().getDecorView();
-            if (rootView.isAttachedToWindow()) {
-                tokens[0] = rootView.getWindowToken();
-                hostTokenReady.countDown();
-            } else {
-                rootView.getViewTreeObserver().addOnWindowAttachListener(
-                        new ViewTreeObserver.OnWindowAttachListener() {
-                            @Override
-                            public void onWindowAttached() {
-                                tokens[0] = rootView.getWindowToken();
-                                hostTokenReady.countDown();
-                            }
-
-                            @Override
-                            public void onWindowDetached() {
-                            }
-                        });
-            }
-        });
-
-        assertTrue("Failed to wait for host to get added",
-                hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
-
-        mInstrumentation.runOnMainSync(() -> {
-            WindowManager wm = mActivity.getSystemService(WindowManager.class);
-
-            View childView = new View(mActivity) {
-                @Override
-                protected void onAttachedToWindow() {
-                    super.onAttachedToWindow();
-                    tokens[1] = getWindowToken();
-                }
-            };
-            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-            params.token = tokens[0];
-            params.type = TYPE_APPLICATION_PANEL;
-            wm.addView(childView, params);
-        });
-
-        boolean[] foundTrusted = new boolean[2];
-
-        CtsWindowInfoUtils.waitForWindowInfos(
-                windowInfos -> {
-                    for (var windowInfo : windowInfos) {
-                        if (windowInfo.windowToken == tokens[0]
-                                && windowInfo.isTrustedOverlay) {
-                            foundTrusted[0] = true;
-                        } else if (windowInfo.windowToken == tokens[1]
-                                && windowInfo.isTrustedOverlay) {
-                            foundTrusted[1] = true;
-                        }
-                    }
-                    return foundTrusted[0] && foundTrusted[1];
-                }, TIMEOUT_S, TimeUnit.SECONDS);
-
-        if (!foundTrusted[0] || !foundTrusted[1]) {
-            CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
-        }
-
-        assertEquals("Failed to find parent window or was not marked trusted", expectTrusted,
-                foundTrusted[0]);
-        assertEquals("Failed to find child window or was not marked trusted", expectTrusted,
-                foundTrusted[1]);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
index f173d66..c5dd447 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java
@@ -18,16 +18,16 @@
 
 import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule;
 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
-
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
 import android.platform.test.annotations.Presubmit;
-import android.util.Log;
+import android.server.wm.CtsWindowInfoUtils;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.TrustedPresentationThresholds;
 
+import androidx.annotation.GuardedBy;
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
 import com.android.server.wm.utils.CommonUtils;
@@ -36,9 +36,8 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 /**
@@ -53,6 +52,15 @@
 
     private static final float FRACTION_VISIBLE = 0.1f;
 
+    private final Object mResultsLock = new Object();
+    @GuardedBy("mResultsLock")
+    private boolean mResult;
+    @GuardedBy("mResultsLock")
+    private boolean mReceivedResults;
+
+    @Rule
+    public TestName mName = new TestName();
+
     @Rule
     public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule(
             TestActivity.class);
@@ -71,36 +79,32 @@
 
     @Test
     public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException {
-        boolean[] results = new boolean[1];
-        CountDownLatch receivedResults = new CountDownLatch(1);
         TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
                 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds,
                 Runnable::run, inTrustedPresentationState -> {
-                    Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState);
-                    results[0] = inTrustedPresentationState;
-                    receivedResults.countDown();
+                    synchronized (mResultsLock) {
+                        mResult = inTrustedPresentationState;
+                        mReceivedResults = true;
+                        mResultsLock.notify();
+                    }
                 });
         t.apply();
-
-        assertTrue("Timed out waiting for results",
-                receivedResults.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertTrue(results[0]);
+        synchronized (mResultsLock) {
+            assertResults();
+        }
     }
 
     @Test
     public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException {
-        final Object resultsLock = new Object();
-        boolean[] results = new boolean[1];
-        boolean[] receivedResults = new boolean[1];
         TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(
                 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
         Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> {
-            synchronized (resultsLock) {
-                results[0] = inTrustedPresentationState;
-                receivedResults[0] = true;
-                resultsLock.notify();
+            synchronized (mResultsLock) {
+                mResult = inTrustedPresentationState;
+                mReceivedResults = true;
+                mResultsLock.notify();
             }
         };
         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -108,34 +112,43 @@
                 Runnable::run, trustedPresentationCallback);
         t.apply();
 
-        synchronized (resultsLock) {
-            if (!receivedResults[0]) {
-                resultsLock.wait(WAIT_TIME_MS);
+        synchronized (mResultsLock) {
+            if (!mReceivedResults) {
+                mResultsLock.wait(WAIT_TIME_MS);
             }
-            // Make sure we received the results and not just timed out
-            assertTrue("Timed out waiting for results", receivedResults[0]);
-            assertTrue(results[0]);
-
+            assertResults();
             // reset the state
-            receivedResults[0] = false;
+            mReceivedResults = false;
         }
 
         mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t,
                 trustedPresentationCallback);
         t.apply();
 
-        synchronized (resultsLock) {
-            if (!receivedResults[0]) {
-                resultsLock.wait(WAIT_TIME_MS);
+        synchronized (mResultsLock) {
+            if (!mReceivedResults) {
+                mResultsLock.wait(WAIT_TIME_MS);
             }
             // Ensure we waited the full time and never received a notify on the result from the
             // callback.
-            assertFalse("Should never have received a callback", receivedResults[0]);
+            assertFalse("Should never have received a callback", mReceivedResults);
             // results shouldn't have changed.
-            assertTrue(results[0]);
+            assertTrue(mResult);
         }
     }
 
+    @GuardedBy("mResultsLock")
+    private void assertResults() throws InterruptedException {
+        mResultsLock.wait(WAIT_TIME_MS);
+
+        if (!mReceivedResults) {
+            CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName());
+        }
+        // Make sure we received the results and not just timed out
+        assertTrue("Timed out waiting for results", mReceivedResults);
+        assertTrue(mResult);
+    }
+
     public static class TestActivity extends Activity {
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index d85d9b5..2d5b72b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -111,7 +111,7 @@
                 return null;
             }
             final WindowContextInfoChangeItem infoChangeItem = (WindowContextInfoChangeItem) item;
-            infoChangeItem.execute(mHandler, null, null);
+            infoChangeItem.execute(mHandler, null /* pendingActions */);
             return null;
         }).when(mWpc).scheduleClientTransactionItem(any());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index ebe40b0..b89182d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -330,7 +330,7 @@
                 ArgumentCaptor.forClass(ConfigurationChangeItem.class);
         verify(clientManager).scheduleTransaction(eq(thread), captor.capture());
         final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
-        captor.getValue().preExecute(client, null /* token */);
+        captor.getValue().preExecute(client);
         final ArgumentCaptor<Configuration> configCaptor =
                 ArgumentCaptor.forClass(Configuration.class);
         verify(client).updatePendingConfiguration(configCaptor.capture());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 3d78a1d..0a70a5f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -27,9 +27,9 @@
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH;
 
-import android.app.AppOpsManager;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.content.ComponentName;
@@ -50,6 +50,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SharedMemory;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.service.voice.HotwordDetectionService;
 import android.service.voice.HotwordDetectionServiceFailure;
@@ -114,6 +115,9 @@
     private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
     private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
 
+    private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
+            SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
+
     /**
      * Indicates the {@link HotwordDetectionService} is created.
      */
@@ -680,7 +684,8 @@
             mIntent = intent;
             mDetectionServiceType = detectionServiceType;
             int flags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0;
-            if (mVisualQueryDetectionComponentName != null
+            if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED
+                    && mVisualQueryDetectionComponentName != null
                     && mHotwordDetectionComponentName != null) {
                 flags |= Context.BIND_SHARED_ISOLATED_PROCESS;
             }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 471acc1..6ba77da 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -57,6 +57,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SharedMemory;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.service.voice.HotwordDetector;
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
@@ -96,6 +97,8 @@
 
     /** The delay time for retrying to request DirectActions. */
     private static final long REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS = 200;
+    private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
+            SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
 
     final boolean mValid;
 
@@ -715,7 +718,7 @@
         } else {
             verifyDetectorForVisualQueryDetectionLocked(sharedMemory);
         }
-        if (!verifyProcessSharingLocked()) {
+        if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED && !verifyProcessSharingLocked()) {
             Slog.w(TAG, "Sandboxed detection service not in shared isolated process");
             throw new IllegalStateException("VisualQueryDetectionService or HotworDetectionService "
                     + "not in a shared isolated process. Please make sure to set "
@@ -914,6 +917,7 @@
         if (hotwordInfo == null || visualQueryInfo == null) {
             return true;
         }
+        // Enforce shared isolated option is used when VisualQueryDetectionservice is enabled
         return (hotwordInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0
                 && (visualQueryInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0;
     }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72f780..1f32c97 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -412,6 +412,15 @@
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
 
     /**
+     * Optional extra for incoming containing a long which specifies the time the
+     * call was answered by user. This value is in milliseconds.
+     * @hide
+     */
+    public static final String EXTRA_CALL_ANSWERED_TIME_MILLIS =
+            "android.telecom.extra.CALL_ANSWERED_TIME_MILLIS";
+
+
+    /**
      * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
      * time the call was created.
      * @hide
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 66f3bed..36a8fc1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4607,12 +4607,12 @@
      *
      * <p>Example:
      *
-     * <pre><code>
+     * <pre>{@code
      * <string-array name="carrier_service_name_array" num="2">
      *   <item value="Police"/>
      *   <item value="Ambulance"/>
      * </string-array>
-     * </code></pre>
+     * }</pre>
      */
     public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
 
@@ -4626,18 +4626,18 @@
      *
      * <ul>
      *   <li>The number of items in both the arrays are equal
-     *   <li>The item added in this key follows a specific format. Either it should be all numbers,
-     *       or "+" followed by all numbers.
+     *   <li>The item should contain dialable characters only which includes 0-9, -, *, #, (, ),
+     *       SPACE.
      * </ul>
      *
      * <p>Example:
      *
-     * <pre><code>
+     * <pre>{@code
      * <string-array name="carrier_service_number_array" num="2">
-     *   <item value="123"/>
-     *   <item value="+343"/>
+     *   <item value="*123"/>
+     *   <item value="+ (111) 111-111"/>
      * </string-array>
-     * </code></pre>
+     * }</pre>
      */
     public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY =
         "carrier_service_number_array";
@@ -10229,7 +10229,7 @@
         sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
         sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
         sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
-        sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, true);
+        sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, false);
         sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
                 new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
         sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index f1af68f..53f347e 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -71,13 +71,6 @@
      */
     public static final int INVALID = Integer.MAX_VALUE;
 
-    private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
-
-    private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
-
-    /* The type of signal measurement */
-    private static final String MEASUREMENT_TYPE_RSCP = "rscp";
-
     // Timestamp of SignalStrength since boot
     // Effectively final. Timestamp is set during construction of SignalStrength
     private long mTimestampMillis;
@@ -92,28 +85,9 @@
     CellSignalStrengthNr mNr;
 
     /**
-     * Create a new SignalStrength from a intent notifier Bundle
-     *
-     * This method may be used by external applications.
-     *
-     * @param m Bundle from intent notifier
-     * @return newly created SignalStrength
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static SignalStrength newFromBundle(Bundle m) {
-        SignalStrength ret;
-        ret = new SignalStrength();
-        ret.setFromNotifierBundle(m);
-        return ret;
-    }
-
-    /**
      * This constructor is used to create SignalStrength with default
      * values.
      *
-     * @return newly created SignalStrength
      * @hide
      */
     @UnsupportedAppUsage
@@ -164,20 +138,20 @@
      * Returns a List of CellSignalStrength Components of this SignalStrength Report.
      *
      * Use this API to access underlying
-     * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+     * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
      * granular information about the SignalStrength report. Only valid (non-empty)
      * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
      * and the list may contain more than one instance of a CellSignalStrength type.
      *
      * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
      *
-     * @see android.telephony#CellSignalStrength
-     * @see android.telephony#CellSignalStrengthNr
-     * @see android.telephony#CellSignalStrengthLte
-     * @see android.telephony#CellSignalStrengthTdscdma
-     * @see android.telephony#CellSignalStrengthWcdma
-     * @see android.telephony#CellSignalStrengthCdma
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrength
+     * @see android.telephony.CellSignalStrengthNr
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthGsm
      */
     @NonNull public List<CellSignalStrength> getCellSignalStrengths() {
         return getCellSignalStrengths(CellSignalStrength.class);
@@ -187,7 +161,7 @@
      * Returns a List of CellSignalStrength Components of this SignalStrength Report.
      *
      * Use this API to access underlying
-     * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+     * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
      * granular information about the SignalStrength report. Only valid (non-empty)
      * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
      * and the list may contain more than one instance of a CellSignalStrength type.
@@ -197,13 +171,13 @@
      *        return values.
      * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
      *
-     * @see android.telephony#CellSignalStrength
-     * @see android.telephony#CellSignalStrengthNr
-     * @see android.telephony#CellSignalStrengthLte
-     * @see android.telephony#CellSignalStrengthTdscdma
-     * @see android.telephony#CellSignalStrengthWcdma
-     * @see android.telephony#CellSignalStrengthCdma
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrength
+     * @see android.telephony.CellSignalStrengthNr
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthGsm
      */
     @NonNull public <T extends CellSignalStrength> List<T> getCellSignalStrengths(
             @NonNull Class<T> clazz) {
@@ -319,7 +293,7 @@
      *
      */
     public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR =
-            new Parcelable.Creator<SignalStrength>() {
+            new Parcelable.Creator<>() {
                 public SignalStrength createFromParcel(Parcel in) {
                     return new SignalStrength(in);
                 }
@@ -327,7 +301,7 @@
                 public SignalStrength[] newArray(int size) {
                     return new SignalStrength[size];
                 }
-    };
+            };
 
     /**
      * Get the GSM RSSI in ASU.
@@ -338,7 +312,7 @@
      *
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthGsm#getAsuLevel}.
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrengthGsm
      * @see android.telephony.SignalStrength#getCellSignalStrengths
      */
     @Deprecated
@@ -352,7 +326,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthGsm#getBitErrorRate}.
      *
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrengthGsm
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      */
     @Deprecated
@@ -368,7 +342,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getCdmaDbm}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      */
     @Deprecated
@@ -382,7 +356,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getCdmaEcio}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      */
     @Deprecated
@@ -398,7 +372,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getEvdoDbm}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      */
     @Deprecated
@@ -412,7 +386,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getEvdoEcio}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      */
     @Deprecated
@@ -426,7 +400,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getEvdoSnr}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      */
     @Deprecated
@@ -438,7 +412,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getRssi}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -452,7 +426,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getRsrp}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -466,7 +440,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getRsrq}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -480,7 +454,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getRssnr}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -494,7 +468,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getCqi}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -527,7 +501,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrength#getAsuLevel}. Because the levels vary by technology,
      *             this method is misleading and should not be used.
-     * @see android.telephony#CellSignalStrength
+     * @see android.telephony.CellSignalStrength
      * @see android.telephony.SignalStrength#getCellSignalStrengths
      * @hide
      */
@@ -543,7 +517,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrength#getDbm()}. Because the levels vary by technology,
      *             this method is misleading and should not be used.
-     * @see android.telephony#CellSignalStrength
+     * @see android.telephony.CellSignalStrength
      * @see android.telephony.SignalStrength#getCellSignalStrengths
      * @hide
      */
@@ -559,7 +533,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthGsm#getDbm}.
      *
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrengthGsm
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -575,7 +549,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthGsm#getLevel}.
      *
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrengthGsm
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -591,7 +565,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthGsm#getAsuLevel}.
      *
-     * @see android.telephony#CellSignalStrengthGsm
+     * @see android.telephony.CellSignalStrengthGsm
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -607,7 +581,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getLevel}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -625,7 +599,7 @@
      *             ASU for CDMA, the resultant value is Android-specific and is not recommended
      *             for use.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -641,7 +615,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthCdma#getEvdoLevel}.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -659,7 +633,7 @@
      *             ASU for EvDO, the resultant value is Android-specific and is not recommended
      *             for use.
      *
-     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthCdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -675,7 +649,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getDbm}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -691,7 +665,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getLevel}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -708,7 +682,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthLte#getAsuLevel}.
      *
-     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthLte
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -739,7 +713,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthTdscdma#getDbm}.
      *
-     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthTdscdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -758,7 +732,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthTdscdma#getLevel}.
      *
-     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthTdscdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -774,7 +748,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthTdscdma#getAsuLevel}.
      *
-     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthTdscdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -790,7 +764,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthWcdma#getRscp}.
      *
-     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthWcdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -805,7 +779,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthWcdma#getAsuLevel}.
      *
-     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthWcdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -828,7 +802,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthWcdma#getDbm}.
      *
-     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthWcdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -843,7 +817,7 @@
      * @deprecated this information should be retrieved from
      *             {@link CellSignalStrengthWcdma#getDbm}.
      *
-     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthWcdma
      * @see android.telephony.SignalStrength#getCellSignalStrengths()
      * @hide
      */
@@ -895,32 +869,12 @@
     }
 
     /**
-     * Set SignalStrength based on intent notifier map
-     *
-     * @param m intent notifier map
-     *
-     * @deprecated this method relies on non-stable implementation details, and full access to
-     *             internal storage is available via {@link getCellSignalStrengths()}.
-     * @hide
-     */
-    @Deprecated
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    private void setFromNotifierBundle(Bundle m) {
-        mCdma = m.getParcelable("Cdma", android.telephony.CellSignalStrengthCdma.class);
-        mGsm = m.getParcelable("Gsm", android.telephony.CellSignalStrengthGsm.class);
-        mWcdma = m.getParcelable("Wcdma", android.telephony.CellSignalStrengthWcdma.class);
-        mTdscdma = m.getParcelable("Tdscdma", android.telephony.CellSignalStrengthTdscdma.class);
-        mLte = m.getParcelable("Lte", android.telephony.CellSignalStrengthLte.class);
-        mNr = m.getParcelable("Nr", android.telephony.CellSignalStrengthNr.class);
-    }
-
-    /**
      * Set intent notifier Bundle based on SignalStrength
      *
      * @param m intent notifier Bundle
      *
      * @deprecated this method relies on non-stable implementation details, and full access to
-     *             internal storage is available via {@link getCellSignalStrengths()}.
+     *             internal storage is available via {@link #getCellSignalStrengths()}.
      * @hide
      */
     @Deprecated
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index faf97b5..de2e952 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -18,6 +18,7 @@
 
 import static android.text.TextUtils.formatSimple;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -44,6 +45,7 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
@@ -255,6 +257,11 @@
     private final boolean mIsGroupDisabled;
 
     /**
+     * Whether this subscription is used for communicating with non-terrestrial networks.
+     */
+    private final boolean mIsNtn;
+
+    /**
      * @hide
      *
      * @deprecated Use {@link SubscriptionInfo.Builder}.
@@ -378,6 +385,7 @@
         this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
         this.mPortIndex = portIndex;
         this.mUsageSetting = usageSetting;
+        this.mIsNtn = false;
     }
 
     /**
@@ -416,6 +424,7 @@
         this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
         this.mPortIndex = builder.mPortIndex;
         this.mUsageSetting = builder.mUsageSetting;
+        this.mIsNtn = builder.mIsNtn;
     }
 
     /**
@@ -862,6 +871,17 @@
         return mUsageSetting;
     }
 
+    /**
+     * Check if the subscription is exclusively for non-terrestrial networks.
+     *
+     * @return {@code true} if it is a non-terrestrial network subscription, {@code false}
+     * otherwise.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isNtn() {
+        return mIsNtn;
+    }
+
     @NonNull
     public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
             new Parcelable.Creator<SubscriptionInfo>() {
@@ -898,6 +918,7 @@
                             UiccAccessRule.CREATOR))
                     .setUiccApplicationsEnabled(source.readBoolean())
                     .setUsageSetting(source.readInt())
+                    .setNtn(source.readBoolean())
                     .build();
         }
 
@@ -939,6 +960,7 @@
         dest.writeTypedArray(mCarrierConfigAccessRules, flags);
         dest.writeBoolean(mAreUiccApplicationsEnabled);
         dest.writeInt(mUsageSetting);
+        dest.writeBoolean(mIsNtn);
     }
 
     @Override
@@ -1001,6 +1023,7 @@
                 + " mType=" + SubscriptionManager.subscriptionTypeToString(mType)
                 + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
                 + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+                + " ntn=" + mIsNtn
                 + "]";
     }
 
@@ -1025,7 +1048,8 @@
                 that.mCardString) && Arrays.equals(mNativeAccessRules,
                 that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
                 that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
-                && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner);
+                && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
+                && mIsNtn == that.mIsNtn;
     }
 
     @Override
@@ -1034,7 +1058,7 @@
                 mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
                 mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
                 mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
-                mIsGroupDisabled);
+                mIsGroupDisabled, mIsNtn);
         result = 31 * result + Arrays.hashCode(mEhplmns);
         result = 31 * result + Arrays.hashCode(mHplmns);
         result = 31 * result + Arrays.hashCode(mNativeAccessRules);
@@ -1234,6 +1258,11 @@
         private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
 
         /**
+         * {@code true} if it is a non-terrestrial network subscription, {@code false} otherwise.
+         */
+        private boolean mIsNtn = false;
+
+        /**
          * Default constructor.
          */
         public Builder() {
@@ -1275,6 +1304,7 @@
             mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
             mPortIndex = info.mPortIndex;
             mUsageSetting = info.mUsageSetting;
+            mIsNtn = info.mIsNtn;
         }
 
         /**
@@ -1660,6 +1690,18 @@
         }
 
         /**
+         * Set whether the subscription is exclusively used for non-terrestrial networks or not.
+         *
+         * @param isNtn {@code true} if the subscription is for NTN, {@code false} otherwise.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNtn(boolean isNtn) {
+            mIsNtn = isNtn;
+            return this;
+        }
+
+        /**
          * Build the {@link SubscriptionInfo}.
          *
          * @return The {@link SubscriptionInfo} instance.
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 6d9c1e6..29149b9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1117,6 +1117,14 @@
     public static final String SATELLITE_ATTACH_ENABLED_FOR_CARRIER =
             SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 
+    /**
+     * TelephonyProvider column name to identify eSIM profile of a non-terrestrial network.
+     * By default, it's disabled.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String IS_NTN = SimInfo.COLUMN_IS_NTN;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"USAGE_SETTING_"},
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 5f6c14a..a4527f12 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -39,6 +40,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.IVoidConsumer;
+import com.android.internal.telephony.flags.Flags;
 import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
@@ -722,6 +724,17 @@
      */
     public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7;
     /**
+     * A transition state indicating that Telephony is waiting for satellite modem to connect to a
+     * satellite network before sending a datagram or polling for datagrams. If the satellite modem
+     * successfully connects to a satellite network, either
+     * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING} or
+     * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING} will be sent. Otherwise,
+     * either {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED} or
+     * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED} will be sent.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8;
+    /**
      * The datagram transfer state is unknown. This generic datagram transfer state should be used
      * only when the datagram transfer state cannot be mapped to other specific datagram transfer
      * states.
@@ -738,6 +751,7 @@
             SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
             SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE,
             SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
             SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -769,6 +783,16 @@
      */
     public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5;
     /**
+     * The satellite modem is powered on but the device is not registered to a satellite cell.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6;
+    /**
+     * The satellite modem is powered on and the device is registered to a satellite cell.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_CONNECTED = 7;
+    /**
      * Satellite modem state is unknown. This generic modem state should be used only when the
      * modem state cannot be mapped to other specific modem states.
      */
@@ -782,6 +806,8 @@
             SATELLITE_MODEM_STATE_DATAGRAM_RETRYING,
             SATELLITE_MODEM_STATE_OFF,
             SATELLITE_MODEM_STATE_UNAVAILABLE,
+            SATELLITE_MODEM_STATE_NOT_CONNECTED,
+            SATELLITE_MODEM_STATE_CONNECTED,
             SATELLITE_MODEM_STATE_UNKNOWN
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
index e4f9413..162fe2b 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
@@ -46,6 +46,14 @@
      */
     SATELLITE_MODEM_STATE_UNAVAILABLE = 5,
     /**
+     * The satellite modem is powered on but the device is not registered to a satellite cell.
+     */
+    SATELLITE_MODEM_STATE_NOT_CONNECTED = 6,
+    /**
+     * The satellite modem is powered on and the device is registered to a satellite cell.
+     */
+    SATELLITE_MODEM_STATE_CONNECTED = 7,
+    /**
      * Satellite modem state is unknown. This generic modem state should be used only when the
      * modem state cannot be mapped to other specific modem states.
      */
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 21a6b44..a5a23e8 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -323,49 +323,49 @@
      */
      UserHandle getSubscriptionUserHandle(int subId);
 
-     /**
-      * Check if subscription and user are associated with each other.
-      *
-      * @param subscriptionId the subId of the subscription
-      * @param userHandle user handle of the user
-      * @return {@code true} if subscription is associated with user
-      * {code true} if there are no subscriptions on device
-      * else {@code false} if subscription is not associated with user.
-      *
-      * @throws IllegalArgumentException if subscription is invalid.
-      * @throws SecurityException if the caller doesn't have permissions required.
-      * @throws IllegalStateException if subscription service is not available.
-      *
-      * @hide
-      */
-      boolean isSubscriptionAssociatedWithUser(int subscriptionId, in UserHandle userHandle);
+    /**
+     * Check if subscription and user are associated with each other.
+     *
+     * @param subscriptionId the subId of the subscription
+     * @param userHandle user handle of the user
+     * @return {@code true} if subscription is associated with user
+     * {code true} if there are no subscriptions on device
+     * else {@code false} if subscription is not associated with user.
+     *
+     * @throws IllegalArgumentException if subscription is invalid.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws IllegalStateException if subscription service is not available.
+     *
+     * @hide
+     */
+    boolean isSubscriptionAssociatedWithUser(int subscriptionId, in UserHandle userHandle);
 
-      /**
-       * Get list of subscriptions associated with user.
-       *
-       * @param userHandle user handle of the user
-       * @return list of subscriptionInfo associated with the user.
-       *
-       * @throws SecurityException if the caller doesn't have permissions required.
-       * @throws IllegalStateException if subscription service is not available.
-       *
-       * @hide
-       */
-       List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle);
+    /**
+     * Get list of subscriptions associated with user.
+     *
+     * @param userHandle user handle of the user
+     * @return list of subscriptionInfo associated with the user.
+     *
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws IllegalStateException if subscription service is not available.
+     *
+     * @hide
+     */
+    List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle);
 
-      /**
-       * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
-       * configs to device for all existing SIMs in the subscription database
-       * {@link Telephony.SimInfo}. Internally, it will store the backup data in an internal file.
-       * This file will persist on device for device's lifetime and will be used later on when a SIM
-       * is inserted to restore that specific SIM's settings. End result is subscription database is
-       * modified to match any backed up configs for the appropriate inserted SIMs.
-       *
-       * <p>
-       * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any
-       * {@link Telephony.SimInfo} entry is updated as the result of this method call.
-       *
-       * @param data with the sim specific configs to be backed up.
-       */
-       void restoreAllSimSpecificSettingsFromBackup(in byte[] data);
+    /**
+     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+     * configs to device for all existing SIMs in the subscription database
+     * {@link Telephony.SimInfo}. Internally, it will store the backup data in an internal file.
+     * This file will persist on device for device's lifetime and will be used later on when a SIM
+     * is inserted to restore that specific SIM's settings. End result is subscription database is
+     * modified to match any backed up configs for the appropriate inserted SIMs.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any
+     * {@link Telephony.SimInfo} entry is updated as the result of this method call.
+     *
+     * @param data with the sim specific configs to be backed up.
+     */
+    void restoreAllSimSpecificSettingsFromBackup(in byte[] data);
 }
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
index bb10658..e74a30c 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
+++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
 # Lint as: python3
 """
 Base class for setting up devices for CDM functionalities.
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
index 5516c0f..bf7e1f3c9 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
+++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
 # Lint as: python3
 """
 Test E2E CDM functions on mobly.
diff --git a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
index de0ad59..0b75eb3 100644
--- a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
+++ b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "CtsSurfaceControlTestsStaging"
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 87231c8..93a5bf5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -27,7 +27,11 @@
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.utils.*
+import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesVisible
 import org.junit.FixMethodOrder
 import org.junit.Ignore
 import org.junit.Test
@@ -63,11 +67,12 @@
                     .withHomeActivityVisible()
                     .waitForAndVerify()
             startDisplayBounds =
-                    wmHelper.currentState.layerState.physicalDisplayBounds ?:
-                    error("Display not found")
+                    wmHelper.currentState.layerState.physicalDisplayBounds
+                        ?: error("Display not found")
         }
         transitions {
-            SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp)
+            SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp,
+                flicker.scenario.startRotation)
             SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp)
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index c090415..ee22d07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.quickswitch
 
-import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.common.datatypes.Rect
 import android.tools.common.traces.component.ComponentNameMatcher
@@ -51,6 +50,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 298544839)
 class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
     private val testApp1 = SimpleAppHelper(instrumentation)
     private val testApp2 = NonResizeableAppHelper(instrumentation)
@@ -92,7 +92,6 @@
      * Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
      * entirety of the display.
      */
-    @Presubmit
     @Test
     open fun startsWithApp1WindowsCoverFullScreen() {
         flicker.assertWmStart {
@@ -112,7 +111,6 @@
     }
 
     /** Checks that the transition starts with [testApp1] being the top window. */
-    @Presubmit
     @Test
     open fun startsWithApp1WindowBeingOnTop() {
         flicker.assertWmStart { this.isAppWindowOnTop(testApp1) }
@@ -122,7 +120,6 @@
      * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of
      * the transition once we have fully quick switched from [testApp1] back to the [testApp2].
      */
-    @Presubmit
     @Test
     open fun endsWithApp2WindowsCoveringFullScreen() {
         flicker.assertWmEnd { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) }
@@ -132,7 +129,6 @@
      * Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
      * transition once we have fully quick switched from [testApp1] back to the [testApp2].
      */
-    @Presubmit
     @Test
     open fun endsWithApp2LayersCoveringFullScreen() {
         flicker.assertLayersEnd {
@@ -145,7 +141,6 @@
      * Checks that [testApp2] is the top window at the end of the transition once we have fully
      * quick switched from [testApp1] back to the [testApp2].
      */
-    @Presubmit
     @Test
     open fun endsWithApp2BeingOnTop() {
         flicker.assertWmEnd { this.isAppWindowOnTop(testApp2) }
@@ -155,7 +150,6 @@
      * Checks that [testApp2]'s window starts off invisible and becomes visible at some point before
      * the end of the transition and then stays visible until the end of the transition.
      */
-    @Presubmit
     @Test
     open fun app2WindowBecomesAndStaysVisible() {
         flicker.assertWm {
@@ -171,7 +165,6 @@
      * Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
      * the end of the transition and then stays visible until the end of the transition.
      */
-    @Presubmit
     @Test
     open fun app2LayerBecomesAndStaysVisible() {
         flicker.assertLayers { this.isInvisible(testApp2).then().isVisible(testApp2) }
@@ -181,7 +174,6 @@
      * Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
      * the end of the transition and then stays invisible until the end of the transition.
      */
-    @Presubmit
     @Test
     open fun app1WindowBecomesAndStaysInvisible() {
         flicker.assertWm { this.isAppWindowVisible(testApp1).then().isAppWindowInvisible(testApp1) }
@@ -191,7 +183,6 @@
      * Checks that [testApp1]'s layer starts off visible and becomes invisible at some point before
      * the end of the transition and then stays invisible until the end of the transition.
      */
-    @Presubmit
     @Test
     open fun app1LayerBecomesAndStaysInvisible() {
         flicker.assertLayers { this.isVisible(testApp1).then().isInvisible(testApp1) }
@@ -202,7 +193,6 @@
      * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
      * visible.
      */
-    @Presubmit
     @Test
     open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
         flicker.assertWm {
@@ -221,7 +211,6 @@
      * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
      * visible.
      */
-    @Presubmit
     @Test
     open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
         flicker.assertLayers {
@@ -236,7 +225,6 @@
     }
 
     /** {@inheritDoc} */
-    @Presubmit
     @Test
     override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
 
@@ -245,14 +233,34 @@
     @Test
     override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
-    @FlakyTest(bugId = 246284708)
     @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @FlakyTest(bugId = 250518877)
+    @Test override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+    @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
     @Test
-    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+    override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+    @Test
+    override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
+
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+        super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+    @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+    @Test override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
index 4941eea..3cae1c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
@@ -30,6 +30,7 @@
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 
 @Ignore("Base Test Class")
 abstract class QuickSwitchBetweenTwoAppsForward(val rotation: Rotation = Rotation.ROTATION_0) {
@@ -46,7 +47,9 @@
         tapl.setExpectedRotation(rotation.value)
 
         testApp1.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
         testApp2.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
         tapl.launchedAppState.quickSwitchToPreviousApp()
         wmHelper
             .StateSyncBuilder()
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 96b685d..365e00e 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -26,6 +26,7 @@
         "androidx.test.runner",
         "androidx.test.uiautomator_uiautomator",
         "servicestests-utils",
+        "flag-junit",
         "frameworks-base-testutils",
         "hamcrest-library",
         "kotlin-test",
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
new file mode 100644
index 0000000..3a2a3be
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -0,0 +1,69 @@
+/*
+ * 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 android.hardware.input
+
+import android.content.ContextWrapper
+import android.graphics.drawable.Drawable
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.hardware.input.Flags
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for Keyboard layout preview
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardLayoutPreviewTests
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyboardLayoutPreviewTests {
+
+    companion object {
+        const val WIDTH = 100
+        const val HEIGHT = 100
+    }
+
+    @get:Rule
+    val setFlagsRule = SetFlagsRule()
+
+    private fun createDrawable(): Drawable? {
+        val context = ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())
+        val inputManager = context.getSystemService(InputManager::class.java)!!
+        return inputManager.getKeyboardLayoutPreview(null, WIDTH, HEIGHT)
+    }
+
+    @Test
+    fun testKeyboardLayoutDrawable_hasCorrectDimensions() {
+        setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+        val drawable = createDrawable()!!
+        assertEquals(WIDTH, drawable.intrinsicWidth)
+        assertEquals(HEIGHT, drawable.intrinsicHeight)
+    }
+
+    @Test
+    fun testKeyboardLayoutDrawable_isNull_ifFlagOff() {
+        setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+        assertNull(createDrawable())
+    }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
new file mode 100644
index 0000000..eee486f
--- /dev/null
+++ b/tests/InputScreenshotTest/Android.bp
@@ -0,0 +1,60 @@
+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"],
+}
+
+android_test {
+    name: "InputScreenshotTests",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    static_libs: [
+        "androidx.arch.core_core-testing",
+        "androidx.compose.ui_ui-test-junit4",
+        "androidx.compose.ui_ui-test-manifest",
+        "androidx.lifecycle_lifecycle-runtime-testing",
+        "androidx.compose.animation_animation",
+        "androidx.compose.material3_material3",
+        "androidx.compose.material_material-icons-extended",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.runtime_runtime-livedata",
+        "androidx.compose.ui_ui-tooling-preview",
+        "androidx.lifecycle_lifecycle-livedata-ktx",
+        "androidx.lifecycle_lifecycle-runtime-compose",
+        "androidx.navigation_navigation-compose",
+        "truth-prebuilt",
+        "androidx.compose.runtime_runtime",
+        "androidx.test.core",
+        "androidx.test.ext.junit",
+        "androidx.test.ext.truth",
+        "androidx.test.rules",
+        "androidx.test.runner",
+        "androidx.test.uiautomator_uiautomator",
+        "servicestests-utils",
+        "frameworks-base-testutils",
+        "platform-screenshot-diff-core",
+        "hamcrest-library",
+        "kotlin-test",
+        "flag-junit",
+        "platform-test-annotations",
+        "services.core.unboosted",
+        "testables",
+        "testng",
+        "truth-prebuilt",
+    ],
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+    ],
+    test_suites: ["device-tests"],
+    compile_multilib: "both",
+    use_embedded_native_libs: false,
+    asset_dirs: ["assets"],
+}
diff --git a/tests/InputScreenshotTest/AndroidManifest.xml b/tests/InputScreenshotTest/AndroidManifest.xml
new file mode 100644
index 0000000..9ffbb3a
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.input.screenshot">
+
+    <uses-sdk android:minSdkVersion="21"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Screenshot tests for Input"
+        android:targetPackage="com.android.input.screenshot">
+    </instrumentation>
+</manifest>
diff --git a/tests/InputScreenshotTest/AndroidTest.xml b/tests/InputScreenshotTest/AndroidTest.xml
new file mode 100644
index 0000000..cc25fa4
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+<configuration description="Runs Input screendiff tests.">
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <option name="test-suite-tag" value="apct" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="optimized-property-setting" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="InputScreenshotTests.apk" />
+    </target_preparer>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys"
+                value="/data/user/0/com.android.input.screenshot/files/input_screenshots" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.input.screenshot" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/InputScreenshotTest/OWNERS b/tests/InputScreenshotTest/OWNERS
new file mode 100644
index 0000000..3cffce9
--- /dev/null
+++ b/tests/InputScreenshotTest/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136048
+include /core/java/android/hardware/input/OWNERS
diff --git a/tests/InputScreenshotTest/TEST_MAPPING b/tests/InputScreenshotTest/TEST_MAPPING
new file mode 100644
index 0000000..727e609
--- /dev/null
+++ b/tests/InputScreenshotTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "InputScreenshotTests"
+    }
+  ]
+}
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
new file mode 100644
index 0000000..70e4a71
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
new file mode 100644
index 0000000..502c1b4
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
new file mode 100644
index 0000000..591b2fa
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
new file mode 100644
index 0000000..0137a85
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
new file mode 100644
index 0000000..37a91e1
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
new file mode 100644
index 0000000..84c971c
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.input.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.Build
+import android.view.View
+import platform.test.screenshot.matchers.MSSIMMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/** Draw this [View] into a [Bitmap]. */
+// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their
+// tests.
+fun View.drawIntoBitmap(): Bitmap {
+    val bitmap =
+        Bitmap.createBitmap(
+                measuredWidth,
+            measuredHeight,
+            Bitmap.Config.ARGB_8888,
+        )
+    val canvas = Canvas(bitmap)
+    draw(canvas)
+    return bitmap
+}
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ */
+val UnitTestBitmapMatcher =
+    if (Build.CPU_ABI == "x86_64") {
+        // Different CPU architectures can sometimes end up rendering differently, so we can't do
+        // pixel-perfect matching on different architectures using the same golden. Given that our
+        // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
+        // x86_64 architecture and use the Structural Similarity Index on others.
+        // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
+        // do pixel perfect matching both at presubmit time and at development time with actual
+        // devices.
+        PixelPerfectMatcher()
+    } else {
+        MSSIMMatcher()
+    }
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ *
+ * We use the Structural Similarity Index for integration tests because they usually contain
+ * additional information and noise that shouldn't break the test.
+ */
+val IntegrationTestBitmapMatcher = MSSIMMatcher()
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
new file mode 100644
index 0000000..edddc6b4
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.input.screenshot
+
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.DisplaySpec
+
+/**
+ * The emulations specs for all 8 permutations of:
+ * - phone or tablet.
+ * - dark of light mode.
+ * - portrait or landscape.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletFull
+    get() = PhoneAndTabletFullSpec
+
+private val PhoneAndTabletFullSpec =
+        DeviceEmulationSpec.forDisplays(Displays.Phone, Displays.Tablet)
+
+/**
+ * The emulations specs of:
+ * - phone + light mode + portrait.
+ * - phone + light mode + landscape.
+ * - tablet + dark mode + portrait.
+ *
+ * This allows to test the most important permutations of a screen/layout with only 3
+ * configurations.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletMinimal
+    get() = PhoneAndTabletMinimalSpec
+
+private val PhoneAndTabletMinimalSpec =
+    DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false) +
+    DeviceEmulationSpec.forDisplays(Displays.Tablet, isDarkTheme = true, isLandscape = false)
+
+/**
+ * This allows to test only single most important configuration.
+ */
+val DeviceEmulationSpec.Companion.PhoneMinimal
+    get() = PhoneMinimalSpec
+
+private val PhoneMinimalSpec =
+    DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false, isLandscape = false)
+
+object Displays {
+    val Phone =
+        DisplaySpec(
+            "phone",
+            width = 1440,
+            height = 3120,
+            densityDpi = 560,
+        )
+
+    val Tablet =
+        DisplaySpec(
+            "tablet",
+            width = 2560,
+            height = 1600,
+            densityDpi = 320,
+        )
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
new file mode 100644
index 0000000..8faf224
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.input.screenshot
+
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.PathConfig
+
+/** A [GoldenImagePathManager] that should be used for all Input screenshot tests. */
+class InputGoldenImagePathManager(
+        pathConfig: PathConfig,
+        assetsPathRelativeToBuildRoot: String
+) :
+        GoldenImagePathManager(
+                appContext = InstrumentationRegistry.getInstrumentation().context,
+                assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot,
+                deviceLocalPath =
+                    InstrumentationRegistry.getInstrumentation()
+                        .targetContext
+                        .filesDir
+                        .absolutePath
+                        .toString() + "/input_screenshots",
+                pathConfig = pathConfig,
+        ) {
+    override fun toString(): String {
+        // This string is appended to all actual/expected screenshots on the device, so make sure
+        // it is a static value.
+        return "InputGoldenImagePathManager"
+    }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
new file mode 100644
index 0000000..c2c3d55
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.input.screenshot
+
+import android.content.Context
+import android.graphics.Bitmap
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.Image
+import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.graphics.asImageBitmap
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.MaterialYouColorsRule
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** A rule for Input screenshot diff tests. */
+class InputScreenshotTestRule(
+        emulationSpec: DeviceEmulationSpec,
+        assetsPathRelativeToBuildRoot: String
+) : TestRule {
+    private val colorsRule = MaterialYouColorsRule()
+    private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+    private val screenshotRule =
+        ScreenshotTestRule(
+            InputGoldenImagePathManager(
+                getEmulatedDevicePathConfig(emulationSpec),
+                assetsPathRelativeToBuildRoot
+            )
+        )
+    private val composeRule = createAndroidComposeRule<ComponentActivity>()
+    private val delegateRule =
+            RuleChain.outerRule(colorsRule)
+                .around(deviceEmulationRule)
+                .around(screenshotRule)
+                .around(composeRule)
+    private val matcher = UnitTestBitmapMatcher
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return delegateRule.apply(base, description)
+    }
+
+    /**
+     * Compare [content] with the golden image identified by [goldenIdentifier].
+     */
+    fun screenshotTest(
+            goldenIdentifier: String,
+            content: (Context) -> Bitmap,
+    ) {
+        // Make sure that the activity draws full screen and fits the whole display.
+        val activity = composeRule.activity
+        activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) }
+
+        // Set the content using the AndroidComposeRule to make sure that the Activity is set up
+        // correctly.
+        composeRule.setContent {
+            Image(
+                bitmap = content(activity).asImageBitmap(),
+                contentDescription = null,
+            )
+        }
+        composeRule.waitForIdle()
+
+        val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
+        screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
+    }
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
new file mode 100644
index 0000000..e855786
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Ansi physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewAnsiScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+    }
+
+    val setFlagsRule = SetFlagsRule()
+    val screenshotRule = InputScreenshotTestRule(
+            emulationSpec,
+            "frameworks/base/tests/InputScreenshotTest/assets"
+    )
+
+    @get:Rule
+    val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+    @Test
+    fun test() {
+        setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+        screenshotRule.screenshotTest("layout-preview-ansi") {
+            context: Context -> LayoutPreview.createLayoutPreview(
+                context,
+                KeyboardLayout(
+                    "descriptor",
+                    "layout",
+                    /* collection= */null,
+                    /* priority= */0,
+                    LocaleList(Locale.US),
+                    /* layoutType= */0,
+                    /* vid= */0,
+                    /* pid= */0
+                )
+            )
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
new file mode 100644
index 0000000..8ae6dfd
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Iso physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
+    }
+
+    val setFlagsRule = SetFlagsRule()
+    val screenshotRule = InputScreenshotTestRule(
+            emulationSpec,
+            "frameworks/base/tests/InputScreenshotTest/assets"
+    )
+
+    @get:Rule
+    val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+    @Test
+    fun test() {
+        setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+        screenshotRule.screenshotTest("layout-preview") {
+            context: Context -> LayoutPreview.createLayoutPreview(context, null)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
new file mode 100644
index 0000000..5231c14
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for JIS physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewJisScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+    }
+
+    val setFlagsRule = SetFlagsRule()
+    val screenshotRule = InputScreenshotTestRule(
+            emulationSpec,
+            "frameworks/base/tests/InputScreenshotTest/assets"
+    )
+
+    @get:Rule
+    val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+    @Test
+    fun test() {
+        setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+        screenshotRule.screenshotTest("layout-preview-jis") {
+            context: Context -> LayoutPreview.createLayoutPreview(
+                context,
+                KeyboardLayout(
+                    "descriptor",
+                    "layout",
+                    /* collection= */null,
+                    /* priority= */0,
+                    LocaleList(Locale.JAPAN),
+                    /* layoutType= */0,
+                    /* vid= */0,
+                    /* pid= */0
+                )
+            )
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
new file mode 100644
index 0000000..76ee379
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.hardware.input.InputManager
+import android.hardware.input.KeyboardLayout
+import android.util.TypedValue
+import kotlin.math.roundToInt
+
+object LayoutPreview {
+    fun createLayoutPreview(context: Context, layout: KeyboardLayout?): Bitmap {
+        val im = context.getSystemService(InputManager::class.java)!!
+        val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                600.0F, context.getResources().getDisplayMetrics()).roundToInt()
+        val height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                200.0F, context.getResources().getDisplayMetrics()).roundToInt()
+        val drawable = im.getKeyboardLayoutPreview(layout, width, height)!!
+        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(bitmap)
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
+        drawable.draw(canvas)
+        return bitmap
+    }
+}
\ No newline at end of file
diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS
index 4f655e5..55bab7d 100644
--- a/tools/aapt2/OWNERS
+++ b/tools/aapt2/OWNERS
@@ -1,4 +1,4 @@
 set noparent
-toddke@google.com
 zyy@google.com
-patb@google.com
\ No newline at end of file
+patb@google.com
+markpun@google.com
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index acc0ad0..b42f9ee 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -64,27 +64,27 @@
 
 class SoongLintFix:
     """
-    This class creates a command line tool that will
-    apply lint fixes to the platform via the necessary
-    combination of soong and shell commands.
+    This class creates a command line tool that will apply lint fixes to the
+    platform via the necessary combination of soong and shell commands.
 
-    It breaks up these operations into a few "private" methods
-    that are intentionally exposed so experimental code can tweak behavior.
+    It breaks up these operations into a few "private" methods that are
+    intentionally exposed so experimental code can tweak behavior.
 
-    The entry point, `run`, will apply lint fixes using the
-    intermediate `suggested-fixes` directory that soong creates during its
-    invocation of lint.
+    The entry point, `run`, will apply lint fixes using the intermediate
+    `suggested-fixes` directory that soong creates during its invocation of
+    lint.
 
     Basic usage:
     ```
     from soong_lint_fix import SoongLintFix
 
-    SoongLintFix().run()
+    opts = SoongLintFixOptions()
+    opts.parse_args(sys.argv)
+    SoongLintFix(opts).run()
     ```
     """
-    def __init__(self):
-        self._parser = _setup_parser()
-        self._args = None
+    def __init__(self, opts):
+        self._opts = opts
         self._kwargs = None
         self._modules = []
 
@@ -96,19 +96,18 @@
         self._find_modules()
         self._lint()
 
-        if not self._args.no_fix:
+        if not self._opts.no_fix:
             self._fix()
 
-        if self._args.print:
+        if self._opts.print:
             self._print()
 
     def _setup(self):
-        self._args = self._parser.parse_args()
         env = os.environ.copy()
-        if self._args.check:
-            env["ANDROID_LINT_CHECK"] = self._args.check
-        if self._args.lint_module:
-            env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._args.lint_module
+        if self._opts.check:
+            env["ANDROID_LINT_CHECK"] = self._opts.check
+        if self._opts.lint_module:
+            env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module
 
         self._kwargs = {
             "env": env,
@@ -131,7 +130,7 @@
         with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
             module_info = json.load(f)
 
-        for module_name in self._args.modules:
+        for module_name in self._opts.modules:
             module = SoongModule(module_name)
             module.find(module_info)
             self._modules.append(module)
@@ -169,6 +168,20 @@
                 print(f.read())
 
 
+class SoongLintFixOptions:
+    """Options for SoongLintFix"""
+
+    def __init__(self):
+        self.modules = []
+        self.check = None
+        self.lint_module = None
+        self.no_fix = False
+        self.print = False
+
+    def parse_args(self, args=None):
+        _setup_parser().parse_args(args, self)
+
+
 def _setup_parser():
     parser = argparse.ArgumentParser(description="""
         This is a python script that applies lint fixes to the platform:
@@ -199,4 +212,6 @@
     return parser
 
 if __name__ == "__main__":
-    SoongLintFix().run()
+    opts = SoongLintFixOptions()
+    opts.parse_args(sys.argv)
+    SoongLintFix(opts).run()
diff --git a/tools/lint/utils/Android.bp b/tools/lint/utils/Android.bp
index 75e8d68..439c86d 100644
--- a/tools/lint/utils/Android.bp
+++ b/tools/lint/utils/Android.bp
@@ -43,3 +43,9 @@
         "AndroidUtilsLintChecker",
     ],
 }
+
+python_binary_host {
+    name: "enforce_permission_counter",
+    srcs: ["enforce_permission_counter.py"],
+    libs: ["soong_lint_fix"],
+}
diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py
new file mode 100644
index 0000000..b5c2ffe
--- /dev/null
+++ b/tools/lint/utils/enforce_permission_counter.py
@@ -0,0 +1,82 @@
+#  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.
+
+import re
+
+import soong_lint_fix
+
+# Libraries that constitute system_server.
+# It is non-trivial to keep in sync with services/Android.bp as some
+# module are post-processed (e.g, services.core).
+TARGETS = [
+        "services.core.unboosted",
+        "services.accessibility",
+        "services.appprediction",
+        "services.appwidget",
+        "services.autofill",
+        "services.backup",
+        "services.companion",
+        "services.contentcapture",
+        "services.contentsuggestions",
+        "services.coverage",
+        "services.devicepolicy",
+        "services.midi",
+        "services.musicsearch",
+        "services.net",
+        "services.people",
+        "services.print",
+        "services.profcollect",
+        "services.restrictions",
+        "services.searchui",
+        "services.smartspace",
+        "services.systemcaptions",
+        "services.translation",
+        "services.texttospeech",
+        "services.usage",
+        "services.usb",
+        "services.voiceinteraction",
+        "services.wallpapereffectsgeneration",
+        "services.wifi",
+]
+
+
+class EnforcePermissionMigratedCounter:
+    """Wrapper around lint_fix to count the number of AIDL methods annotated."""
+    def run(self):
+        opts = soong_lint_fix.SoongLintFixOptions()
+        opts.check = "AnnotatedAidlCounter"
+        opts.lint_module = "AndroidUtilsLintChecker"
+        opts.no_fix = True
+        opts.modules = TARGETS
+
+        self.linter = soong_lint_fix.SoongLintFix(opts)
+        self.linter.run()
+        self.parse_lint_reports()
+
+    def parse_lint_reports(self):
+        counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 }
+        for module in self.linter._modules:
+            with open(module.lint_report, "r") as f:
+                content = f.read()
+                keys = dict(re.findall(r'(\w+)=(\d+)', content))
+                for key in keys:
+                    counts[key] += int(keys[key])
+        print(counts)
+        total = sum(counts.values())
+        annotated_percent = (1 - (counts["unannotated"] / total)) * 100
+        print("Annotated methods = %.2f%%" % (annotated_percent))
+
+
+if __name__ == "__main__":
+    EnforcePermissionMigratedCounter().run()
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..6bd8595
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..d5d6fb6
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_landscape_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..fe8dc69
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_landscape_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_portrait_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..ccd8a33
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_portrait_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..de84c4a
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..af9d7cf
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_landscape_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..33daab9
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_landscape_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_portrait_credential_view_pin_or_password_emergency_call_button.png
new file mode 100644
index 0000000..14a799c
--- /dev/null
+++ b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_portrait_credential_view_pin_or_password_emergency_call_button.png
Binary files differ
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index d41c019..bc41829 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -23,9 +23,11 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.res.Resources;
 import android.net.wifi.sharedconnectivity.service.ISharedConnectivityCallback;
@@ -35,6 +37,7 @@
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -67,7 +70,7 @@
 @SystemApi
 public class SharedConnectivityManager {
     private static final String TAG = SharedConnectivityManager.class.getSimpleName();
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static final class SharedConnectivityCallbackProxy extends
             ISharedConnectivityCallback.Stub {
@@ -172,6 +175,7 @@
     private final String mServicePackageName;
     private final String mIntentAction;
     private ServiceConnection mServiceConnection;
+    private UserManager mUserManager;
 
     /**
      * Creates a new instance of {@link SharedConnectivityManager}.
@@ -217,12 +221,14 @@
         mContext = context;
         mServicePackageName = servicePackageName;
         mIntentAction = serviceIntentAction;
+        mUserManager = context.getSystemService(UserManager.class);
     }
 
     private void bind() {
         mServiceConnection = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
+                if (DEBUG) Log.i(TAG, "onServiceConnected");
                 mService = ISharedConnectivityService.Stub.asInterface(service);
                 synchronized (mProxyDataLock) {
                     if (!mCallbackProxyCache.isEmpty()) {
@@ -253,9 +259,45 @@
             }
         };
 
-        mContext.bindService(
+        boolean result = mContext.bindService(
                 new Intent().setPackage(mServicePackageName).setAction(mIntentAction),
                 mServiceConnection, Context.BIND_AUTO_CREATE);
+        if (!result) {
+            if (DEBUG) Log.i(TAG, "bindService failed");
+            mServiceConnection = null;
+            if (mUserManager != null && !mUserManager.isUserUnlocked()) {  // In direct boot mode
+                IntentFilter intentFilter = new IntentFilter();
+                intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+                mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+            } else {
+                synchronized (mProxyDataLock) {
+                    if (!mCallbackProxyCache.isEmpty()) {
+                        mCallbackProxyCache.keySet().forEach(
+                                callback -> callback.onRegisterCallbackFailed(
+                                        new IllegalStateException(
+                                                "Failed to bind after user unlock")));
+                        mCallbackProxyCache.clear();
+                    }
+                }
+            }
+        }
+    }
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            context.unregisterReceiver(mBroadcastReceiver);
+            bind();
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public BroadcastReceiver getBroadcastReceiver() {
+        return mBroadcastReceiver;
     }
 
     private void registerCallbackInternal(SharedConnectivityClientCallback callback,
@@ -357,6 +399,13 @@
             return false;
         }
 
+        // Try to unregister the broadcast receiver to guard against memory leaks.
+        try {
+            mContext.unregisterReceiver(mBroadcastReceiver);
+        } catch (IllegalArgumentException e) {
+            // This is fine, it means the receiver was never registered or was already unregistered.
+        }
+
         if (mService == null) {
             boolean shouldUnbind;
             synchronized (mProxyDataLock) {