Merge "Create first resources flag for default locale" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 13847c2..6d74a84 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -48,6 +48,7 @@
     ":android.credentials.flags-aconfig-java{.generated_srcjars}",
     ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
     ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
+    ":aconfig_midi_flags_java_lib{.generated_srcjars}",
     ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
     ":com.android.net.flags-aconfig-java{.generated_srcjars}",
 ]
diff --git a/Android.bp b/Android.bp
index a507465a..0c199a6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -227,7 +227,6 @@
         "android.hardware.radio.messaging-V3-java",
         "android.hardware.radio.modem-V3-java",
         "android.hardware.radio.network-V3-java",
-        "android.hardware.radio.satellite-V1-java",
         "android.hardware.radio.sim-V3-java",
         "android.hardware.radio.voice-V3-java",
         "android.hardware.thermal-V1.0-java-constants",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 9218cc9..da02298 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -59,6 +59,7 @@
 // Extract the impl jar from "framework-minus-apex.ravenwood-base" for subsequent build rules.
 java_genrule_host {
     name: "framework-minus-apex.ravenwood",
+    defaults: ["hoststubgen-for-prototype-only-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [
         ":framework-minus-apex.ravenwood-base{ravenwood.jar}",
@@ -66,5 +67,4 @@
     out: [
         "framework-minus-apex.ravenwood.jar",
     ],
-    visibility: ["//visibility:public"],
 }
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 30b4423..e162100 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -124,7 +124,6 @@
             "packages/modules/Media/apex/aidl/stable",
         ],
     },
-    extensions_info_file: ":sdk-extensions-info",
 }
 
 droidstubs {
@@ -132,13 +131,7 @@
     defaults: ["framework-doc-stubs-sources-default"],
     args: metalava_framework_docs_args +
         " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
-    api_levels_annotations_enabled: true,
-    api_levels_annotations_dirs: [
-        "sdk-dir",
-        "api-versions-jars-dir",
-    ],
-    api_levels_sdk_type: "system",
-    extensions_info_file: ":sdk-extensions-info",
+    api_levels_module: "api_versions_system",
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index fa4bc0f..7e41660 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -695,6 +695,7 @@
         "api-stubs-docs-non-updatable.api.contribution",
     ],
     visibility: ["//visibility:public"],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -710,6 +711,7 @@
         "system-api-stubs-docs-non-updatable.api.contribution",
     ],
     visibility: ["//visibility:public"],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -727,6 +729,7 @@
         "test-api-stubs-docs-non-updatable.api.contribution",
     ],
     visibility: ["//visibility:public"],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -742,6 +745,7 @@
         "api-stubs-docs-non-updatable.api.contribution",
         "system-api-stubs-docs-non-updatable.api.contribution",
     ],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -761,6 +765,7 @@
         "module-lib-api-stubs-docs-non-updatable.api.contribution",
     ],
     visibility: ["//visibility:public"],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -774,6 +779,7 @@
         "stub-annotations",
     ],
     visibility: ["//visibility:public"],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -798,6 +804,7 @@
     visibility: [
         "//visibility:private",
     ],
+    enable_validation: false,
 }
 
 java_api_library {
@@ -814,6 +821,7 @@
         "android_module_lib_stubs_current.from-text",
     ],
     visibility: ["//visibility:public"],
+    enable_validation: false,
 }
 
 ////////////////////////////////////////////////////////////////////////
diff --git a/core/api/current.txt b/core/api/current.txt
index dfe023a..f6564ec 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3286,10 +3286,10 @@
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
-    method @Deprecated public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
-    method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
-    method @Deprecated public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
-    method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+    method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
+    method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+    method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
+    method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method public boolean clearCache();
     method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
     method public final void disableSelf();
@@ -3401,9 +3401,9 @@
     field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
     field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
     field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
-    field public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
-    field public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
-    field public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
+    field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
+    field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
+    field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
     field public static final String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final String SERVICE_META_DATA = "android.accessibilityservice";
     field public static final int SHOW_MODE_AUTO = 0; // 0x0
@@ -9788,7 +9788,7 @@
     method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
     method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
     method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
-    method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
+    method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
     method @NonNull public android.content.AttributionSource.Builder setPid(int);
   }
@@ -25787,15 +25787,15 @@
     method public abstract void onDisconnect(android.media.midi.MidiReceiver);
   }
 
-  public abstract class MidiUmpDeviceService extends android.app.Service {
+  @FlaggedApi("com.android.media.midi.flags.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
     ctor public MidiUmpDeviceService();
-    method @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
-    method @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
-    method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public void onClose();
-    method public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
-    method @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
-    field public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
+    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
+    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
+    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onClose();
+    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
+    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
+    field @FlaggedApi("com.android.media.midi.flags.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9ecce14..c72d09d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3555,7 +3555,7 @@
     field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
     field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
     field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
-    field public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
+    field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
     field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
     field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -4028,7 +4028,7 @@
     field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
     field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
-    field public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
+    field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
     field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0e857a9..b905287 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3185,7 +3185,6 @@
     field public static final int HAL_SERVICE_MESSAGING = 2; // 0x2
     field public static final int HAL_SERVICE_MODEM = 3; // 0x3
     field public static final int HAL_SERVICE_NETWORK = 4; // 0x4
-    field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int HAL_SERVICE_SATELLITE = 8; // 0x8
     field public static final int HAL_SERVICE_SIM = 5; // 0x5
     field public static final int HAL_SERVICE_VOICE = 6; // 0x6
     field public static final android.util.Pair HAL_VERSION_UNKNOWN;
@@ -3585,8 +3584,8 @@
     field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
     field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
     field public CharSequence accessibilityTitle;
-    field public float preferredMaxDisplayRefreshRate;
-    field public float preferredMinDisplayRefreshRate;
+    field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMaxDisplayRefreshRate;
+    field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMinDisplayRefreshRate;
     field public int privateFlags;
   }
 
@@ -3616,7 +3615,6 @@
 
   public final class AccessibilityWindowInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
-    field public static final int UNDEFINED_WINDOW_ID = -1; // 0xffffffff
   }
 
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3370c12..1000612 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -23,6 +23,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.CheckResult;
 import android.annotation.ColorInt;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -793,6 +794,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
+    @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
     @IntDef(
             prefix = {"OVERLAY_RESULT_"},
             value = {
@@ -803,6 +805,7 @@
     public @interface AttachOverlayResult {}
 
     /** Result code indicating the overlay was successfully attached. */
+    @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
     public static final int OVERLAY_RESULT_SUCCESS = 0;
 
     /**
@@ -810,6 +813,7 @@
      * error and not
      * because of problems with the input.
      */
+    @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
     public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1;
 
     /**
@@ -817,6 +821,7 @@
      * specified display or
      * window id was invalid.
      */
+    @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
     public static final int OVERLAY_RESULT_INVALID = 2;
 
     private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -3506,11 +3511,7 @@
      * @param displayId the display to which the SurfaceControl should be attached.
      * @param sc the SurfaceControl containing the overlay content
      *
-     * @deprecated Use
-     * {@link #attachAccessibilityOverlayToDisplay(int, SurfaceControl, Executor, IntConsumer)}
-     * instead.
      */
-    @Deprecated
     public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) {
         Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
         AccessibilityInteractionClient.getInstance(this)
@@ -3547,6 +3548,7 @@
      * @see #OVERLAY_RESULT_INVALID
      * @see #OVERLAY_RESULT_INTERNAL_ERROR
      */
+    @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
     public void attachAccessibilityOverlayToDisplay(
             int displayId,
             @NonNull SurfaceControl sc,
@@ -3581,11 +3583,7 @@
      * @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
      * @param sc the SurfaceControl containing the overlay content
      *
-     * @deprecated Use
-     * {@link #attachAccessibilityOverlayToWindow(int, SurfaceControl, Executor,IntConsumer)}
-     * instead.
      */
-    @Deprecated
     public void attachAccessibilityOverlayToWindow(
             int accessibilityWindowId, @NonNull SurfaceControl sc) {
         Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
@@ -3623,6 +3621,7 @@
      * @see #OVERLAY_RESULT_INVALID
      * @see #OVERLAY_RESULT_INTERNAL_ERROR
      */
+    @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
     public void attachAccessibilityOverlayToWindow(
             int accessibilityWindowId,
             @NonNull SurfaceControl sc,
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 15bd1dc..e268968 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -651,6 +651,7 @@
         // POJO used to override some autofill-related values when the node is parcelized.
         // Not written to parcel.
         AutofillOverlay mAutofillOverlay;
+        boolean mIsCredential;
 
         int mX;
         int mY;
@@ -799,6 +800,7 @@
 
             if (autofillFlags != 0) {
                 mSanitized = in.readInt() == 1;
+                mIsCredential = in.readInt() == 1;
                 mImportantForAutofill = in.readInt();
 
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
@@ -1033,6 +1035,7 @@
 
             if (autofillFlags != 0) {
                 out.writeInt(mSanitized ? 1 : 0);
+                out.writeInt(mIsCredential ? 1 : 0);
                 out.writeInt(mImportantForAutofill);
                 writeSensitive = mSanitized || !sanitizeOnWrite;
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID) != 0) {
@@ -1246,6 +1249,19 @@
         }
 
         /**
+         * @return whether the node is a credential.
+         *
+         * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
+         * not for assist purposes.
+         * TODO(b/303677885): add TestApi
+         *
+         * @hide
+         */
+        public boolean isCredential() {
+            return mIsCredential;
+        }
+
+        /**
          * Gets the {@link android.text.InputType} bits of this structure.
          *
          * @return bits as defined by {@link android.text.InputType}.
@@ -2183,6 +2199,11 @@
         }
 
         @Override
+        public void setIsCredential(boolean isCredential) {
+            mNode.mIsCredential = isCredential;
+        }
+
+        @Override
         public void setReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
             mNode.mReceiveContentMimeTypes = mimeTypes;
         }
@@ -2498,7 +2519,9 @@
                     + ", value=" + node.getAutofillValue()
                     + ", sanitized=" + node.isSanitized()
                     + ", important=" + node.getImportantForAutofill()
-                    + ", visibility=" + node.getVisibility());
+                    + ", visibility=" + node.getVisibility()
+                    + ", isCredential=" + node.isCredential()
+            );
         }
 
         final int NCHILDREN = node.getChildCount();
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 20eae9a..62fbcaf 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -744,6 +744,7 @@
         /**
          * The next app to receive the permission protected data.
          */
+        @FlaggedApi(Flags.FLAG_SET_NEXT_ATTRIBUTION_SOURCE)
         public @NonNull Builder setNextAttributionSource(@NonNull AttributionSource value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x20;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 884351b..59bb73b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -323,7 +323,7 @@
             // Make sure no flag uses the sign bit (most significant bit) of the long integer,
             // to avoid future confusion.
             BIND_BYPASS_USER_NETWORK_RESTRICTIONS,
-            BIND_FILTER_OUT_QUARANTINED_COMPONENTS,
+            BIND_MATCH_QUARANTINED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BindServiceFlagsLongBits {}
@@ -703,7 +703,7 @@
      *
      * @hide
      */
-    public static final long BIND_FILTER_OUT_QUARANTINED_COMPONENTS = 0x2_0000_0000L;
+    public static final long BIND_MATCH_QUARANTINED_COMPONENTS = 0x2_0000_0000L;
 
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1eb2cd1..b765562 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3929,6 +3929,8 @@
      * {@link #ACTION_BOOT_COMPLETED} is sent.  This is sent as a foreground
      * broadcast, since it is part of a visible user interaction; be as quick
      * as possible when handling it.
+     *
+     * <p><b>Note:</b> This broadcast is not sent to the system user.
      */
     public static final String ACTION_USER_INITIALIZE =
             "android.intent.action.USER_INITIALIZE";
@@ -5323,6 +5325,7 @@
      * @hide
      */
     @SystemApi
+    @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
     public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
 
     // ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/ArchivedActivityParcel.aidl b/core/java/android/content/pm/ArchivedActivityParcel.aidl
index 7ab7ed1..74953ff 100644
--- a/core/java/android/content/pm/ArchivedActivityParcel.aidl
+++ b/core/java/android/content/pm/ArchivedActivityParcel.aidl
@@ -16,9 +16,12 @@
 
 package android.content.pm;
 
+import android.content.ComponentName;
+
 /** @hide */
 parcelable ArchivedActivityParcel {
     String title;
+    ComponentName originalComponentName;
     // PNG compressed bitmaps.
     byte[] iconBitmap;
     byte[] monochromeIconBitmap;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4d5d056..1b60f8e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -838,7 +838,7 @@
             GET_DISABLED_COMPONENTS,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
-            FILTER_OUT_QUARANTINED_COMPONENTS,
+            MATCH_QUARANTINED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ComponentInfoFlagsBits {}
@@ -863,7 +863,7 @@
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
             MATCH_CLONE_PROFILE,
-            FILTER_OUT_QUARANTINED_COMPONENTS,
+            MATCH_QUARANTINED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ResolveInfoFlagsBits {}
@@ -1252,12 +1252,13 @@
      */
     // TODO(b/278553670) Unhide and update @links before launch.
     @SystemApi
+    @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
     public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
 
     /**
      * @hide
      */
-    public static final long FILTER_OUT_QUARANTINED_COMPONENTS = 0x100000000L;
+    public static final long MATCH_QUARANTINED_COMPONENTS = 0x100000000L;
 
     /**
      * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index b2cc070..db12728 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -50,3 +50,11 @@
     description: "Feature flag to enable the features that rely on new ART Service APIs that are in the VIC version of the ART module."
     bug: "304741685"
 }
+
+flag {
+    name: "sdk_lib_independence"
+    namespace: "package_manager_service"
+    description: "Feature flag to keep app working even if its declared sdk-library dependency is unavailable."
+    bug: "295827951"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a4593be..aeddd0c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -31,6 +31,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -775,7 +776,8 @@
      */
     public void registerDisplayListener(@NonNull DisplayListener listener,
             @Nullable Handler handler, @EventsMask long eventsMask) {
-        mGlobal.registerDisplayListener(listener, handler, eventsMask);
+        mGlobal.registerDisplayListener(listener, handler, eventsMask,
+                ActivityThread.currentPackageName());
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 6d6085b..8decd50 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -25,6 +25,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
 import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -45,8 +46,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
@@ -72,7 +76,13 @@
  */
 public final class DisplayManagerGlobal {
     private static final String TAG = "DisplayManager";
-    private static final boolean DEBUG = false;
+
+    private static final String EXTRA_LOGGING_PACKAGE_NAME =
+            DisplayProperties.debug_vri_package().orElse(null);
+    private static String sCurrentPackageName = ActivityThread.currentPackageName();
+    private static boolean sExtraDisplayListenerLogging = initExtraLogging();
+
+    private static final boolean DEBUG = false || sExtraDisplayListenerLogging;
 
     // True if display info and display ids should be cached.
     //
@@ -130,6 +140,8 @@
     @VisibleForTesting
     public DisplayManagerGlobal(IDisplayManager dm) {
         mDm = dm;
+        initExtraLogging();
+
         try {
             mWideColorSpace =
                     ColorSpace.get(
@@ -208,7 +220,7 @@
 
         registerCallbackIfNeededLocked();
 
-        if (DEBUG) {
+        if (DEBUG || extraLogging()) {
             Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
         }
         return info;
@@ -321,12 +333,14 @@
      * If null, listener will use the handler for the current thread, and if still null,
      * the handler for the main thread.
      * If that is still null, a runtime exception will be thrown.
+     * @param packageName of the calling package.
      */
     public void registerDisplayListener(@NonNull DisplayListener listener,
-            @Nullable Handler handler, @EventsMask long eventsMask) {
+            @Nullable Handler handler, @EventsMask long eventsMask, String packageName) {
         Looper looper = getLooperForHandler(handler);
         Handler springBoard = new Handler(looper);
-        registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask);
+        registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask,
+                packageName);
     }
 
     /**
@@ -334,9 +348,11 @@
      *
      * @param listener The listener that will be called when display changes occur.
      * @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
+     * @param eventsMask Mask of events to be listened to.
+     * @param packageName of the calling package.
      */
     public void registerDisplayListener(@NonNull DisplayListener listener,
-            @NonNull Executor executor, @EventsMask long eventsMask) {
+            @NonNull Executor executor, @EventsMask long eventsMask, String packageName) {
         if (listener == null) {
             throw new IllegalArgumentException("listener must not be null");
         }
@@ -345,15 +361,22 @@
             throw new IllegalArgumentException("The set of events to listen to must not be empty.");
         }
 
+        if (extraLogging()) {
+            Slog.i(TAG, "Registering Display Listener: "
+                    + Long.toBinaryString(eventsMask) + ", packageName: " + packageName);
+        }
+
         synchronized (mLock) {
             int index = findDisplayListenerLocked(listener);
             if (index < 0) {
-                mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask));
+                mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask,
+                        packageName));
                 registerCallbackIfNeededLocked();
             } else {
                 mDisplayListeners.get(index).setEventsMask(eventsMask);
             }
             updateCallbackIfNeededLocked();
+            maybeLogAllDisplayListeners();
         }
     }
 
@@ -362,6 +385,10 @@
             throw new IllegalArgumentException("listener must not be null");
         }
 
+        if (extraLogging()) {
+            Slog.i(TAG, "Unregistering Display Listener: " + listener);
+        }
+
         synchronized (mLock) {
             int index = findDisplayListenerLocked(listener);
             if (index >= 0) {
@@ -371,6 +398,18 @@
                 updateCallbackIfNeededLocked();
             }
         }
+        maybeLogAllDisplayListeners();
+    }
+
+    private void maybeLogAllDisplayListeners() {
+        if (!sExtraDisplayListenerLogging) {
+            return;
+        }
+
+        Slog.i(TAG, "Currently Registered Display Listeners:");
+        for (int i = 0; i < mDisplayListeners.size(); i++) {
+            Slog.i(TAG, i + ": " + mDisplayListeners.get(i));
+        }
     }
 
     private static Looper getLooperForHandler(@Nullable Handler handler) {
@@ -1148,15 +1187,20 @@
         private final DisplayInfo mDisplayInfo = new DisplayInfo();
         private final Executor mExecutor;
         private AtomicLong mGenerationId = new AtomicLong(1);
+        private final String mPackageName;
 
         DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
-                @EventsMask long eventsMask) {
+                @EventsMask long eventsMask, String packageName) {
             mExecutor = executor;
             mListener = listener;
             mEventsMask = eventsMask;
+            mPackageName = packageName;
         }
 
         public void sendDisplayEvent(int displayId, @DisplayEvent int event, DisplayInfo info) {
+            if (extraLogging()) {
+                Slog.i(TAG, "Sending Display Event: " + eventToString(event));
+            }
             long generationId = mGenerationId.get();
             Message msg = Message.obtain(null, event, displayId, 0, info);
             mExecutor.execute(() -> {
@@ -1177,6 +1221,14 @@
         }
 
         private void handleMessage(Message msg) {
+            if (extraLogging()) {
+                Slog.i(TAG, "DisplayListenerDelegate(" + eventToString(msg.what)
+                        + ", display=" + msg.arg1
+                        + ", mEventsMask=" + Long.toBinaryString(mEventsMask)
+                        + ", mPackageName=" + mPackageName
+                        + ", msg.obj=" + msg.obj
+                        + ", listener=" + mListener.getClass() + ")");
+            }
             if (DEBUG) {
                 Trace.beginSection(
                         "DisplayListenerDelegate(" + eventToString(msg.what)
@@ -1193,6 +1245,10 @@
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
                         DisplayInfo newInfo = (DisplayInfo) msg.obj;
                         if (newInfo != null && !newInfo.equals(mDisplayInfo)) {
+                            if (extraLogging()) {
+                                Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
+                                        + newInfo);
+                            }
                             mDisplayInfo.copyFrom(newInfo);
                             mListener.onDisplayChanged(msg.arg1);
                         }
@@ -1228,6 +1284,11 @@
                 Trace.endSection();
             }
         }
+
+        @Override
+        public String toString() {
+            return "mask: {" + mEventsMask + "}, for " + mListener.getClass();
+        }
     }
 
     /**
@@ -1353,4 +1414,19 @@
         }
         return "UNKNOWN";
     }
+
+
+    private static boolean initExtraLogging() {
+        if (sCurrentPackageName == null) {
+            sCurrentPackageName = ActivityThread.currentPackageName();
+            sExtraDisplayListenerLogging = !TextUtils.isEmpty(EXTRA_LOGGING_PACKAGE_NAME)
+                    && EXTRA_LOGGING_PACKAGE_NAME.equals(sCurrentPackageName);
+        }
+        return sExtraDisplayListenerLogging;
+    }
+
+    private static boolean extraLogging() {
+        return sExtraDisplayListenerLogging && EXTRA_LOGGING_PACKAGE_NAME.equals(
+                sCurrentPackageName);
+    }
 }
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 37559b3..7fceda4 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -16,7 +16,7 @@
 
 flag {
     name: "remove_app_profiler_pss_collection"
-    namespace: "android_platform_power_optimization"
+    namespace: "power_optimization"
     description: "Replaces background PSS collection in AppProfiler with RSS"
     bug: "297542292"
 }
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d8534dd..d60d4c6 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -21,3 +21,10 @@
     description: "enable role controller in system server"
     bug: "302562590"
 }
+
+flag {
+  name: "set_next_attribution_source"
+  namespace: "permissions"
+  description: "enable AttributionSource.setNextAttributionSource"
+  bug: "304478648"
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 3f41c56..d280621 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -520,7 +520,7 @@
             @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AlwaysOnHotwordDetector.Callback callback) {
-        // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+        // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
 
         Objects.requireNonNull(keyphrase);
         Objects.requireNonNull(locale);
@@ -546,6 +546,10 @@
             @NonNull SoundTrigger.ModuleProperties moduleProperties,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AlwaysOnHotwordDetector.Callback callback) {
+        // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+        // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale,
+        // SoundTrigger.ModuleProperties, AlwaysOnHotwordDetector.Callback)} and replace with the
+        // permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
 
         Objects.requireNonNull(keyphrase);
         Objects.requireNonNull(locale);
@@ -612,6 +616,11 @@
             @Nullable PersistableBundle options,
             @Nullable SharedMemory sharedMemory,
             @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+        // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+        // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+        // AlwaysOnHotwordDetector.Callback)} and replace with the permission
+        // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
+
         return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
                 /* supportHotwordDetectionService= */ true, options, sharedMemory,
                 /* modulProperties */ null, /* executor= */ null, callback);
@@ -663,7 +672,11 @@
             @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AlwaysOnHotwordDetector.Callback callback) {
-        // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+        // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+        // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+        // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+        // Executor, AlwaysOnHotwordDetector.Callback)} and replace with the permission
+        // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
 
         Objects.requireNonNull(keyphrase);
         Objects.requireNonNull(locale);
@@ -690,6 +703,10 @@
             @NonNull SoundTrigger.ModuleProperties moduleProperties,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AlwaysOnHotwordDetector.Callback callback) {
+        // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+        // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale, PersistableBundle,
+        // SharedMemory, SoundTrigger.ModuleProperties, Executor, AlwaysOnHotwordDetector.Callback)}
+        // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
 
         Objects.requireNonNull(keyphrase);
         Objects.requireNonNull(locale);
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index a89f795..dc41b70 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -52,6 +52,12 @@
      */
     public static final int RECORD_CONTENT_TASK = 1;
 
+    /** Full screen sharing (app is not selected). */
+    public static final int TARGET_UID_FULL_SCREEN = -1;
+
+    /** Can't report (e.g. side loaded app). */
+    public static final int TARGET_UID_UNKNOWN = -2;
+
     /**
      * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
      * recorded content rendered to its surface.
@@ -89,27 +95,36 @@
      */
     private boolean mWaitingForConsent = false;
 
+    /** UID of the package that is captured if selected. */
+    private int mTargetUid = TARGET_UID_UNKNOWN;
+
     /**
      * Default instance, with recording the display.
      */
     private ContentRecordingSession() {
     }
 
-    /**
-     * Returns an instance initialized for recording the indicated display.
-     */
+    /** Returns an instance initialized for recording the indicated display. */
     public static ContentRecordingSession createDisplaySession(int displayToMirror) {
-        return new ContentRecordingSession().setDisplayToRecord(displayToMirror)
-                .setContentToRecord(RECORD_CONTENT_DISPLAY);
+        return new ContentRecordingSession()
+                .setDisplayToRecord(displayToMirror)
+                .setContentToRecord(RECORD_CONTENT_DISPLAY)
+                .setTargetUid(TARGET_UID_FULL_SCREEN);
     }
 
-    /**
-     * Returns an instance initialized for task recording.
-     */
+    /** Returns an instance initialized for task recording. */
     public static ContentRecordingSession createTaskSession(
             @NonNull IBinder taskWindowContainerToken) {
-        return new ContentRecordingSession().setContentToRecord(RECORD_CONTENT_TASK)
-                .setTokenToRecord(taskWindowContainerToken);
+        return createTaskSession(taskWindowContainerToken, TARGET_UID_UNKNOWN);
+    }
+
+    /** Returns an instance initialized for task recording. */
+    public static ContentRecordingSession createTaskSession(
+            @NonNull IBinder taskWindowContainerToken, int targetUid) {
+        return new ContentRecordingSession()
+                .setContentToRecord(RECORD_CONTENT_TASK)
+                .setTokenToRecord(taskWindowContainerToken)
+                .setTargetUid(targetUid);
     }
 
     /**
@@ -175,13 +190,33 @@
         }
     }
 
+    @IntDef(prefix = "TARGET_UID_", value = {
+        TARGET_UID_FULL_SCREEN,
+        TARGET_UID_UNKNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface TargetUid {}
+
+    @DataClass.Generated.Member
+    public static String targetUidToString(@TargetUid int value) {
+        switch (value) {
+            case TARGET_UID_FULL_SCREEN:
+                    return "TARGET_UID_FULL_SCREEN";
+            case TARGET_UID_UNKNOWN:
+                    return "TARGET_UID_UNKNOWN";
+            default: return Integer.toHexString(value);
+        }
+    }
+
     @DataClass.Generated.Member
     /* package-private */ ContentRecordingSession(
             int virtualDisplayId,
             @RecordContent int contentToRecord,
             int displayToRecord,
             @Nullable IBinder tokenToRecord,
-            boolean waitingForConsent) {
+            boolean waitingForConsent,
+            int targetUid) {
         this.mVirtualDisplayId = virtualDisplayId;
         this.mContentToRecord = contentToRecord;
 
@@ -196,6 +231,7 @@
         this.mDisplayToRecord = displayToRecord;
         this.mTokenToRecord = tokenToRecord;
         this.mWaitingForConsent = waitingForConsent;
+        this.mTargetUid = targetUid;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -251,6 +287,14 @@
     }
 
     /**
+     * UID of the package that is captured if selected.
+     */
+    @DataClass.Generated.Member
+    public int getTargetUid() {
+        return mTargetUid;
+    }
+
+    /**
      * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
      * recorded content rendered to its surface.
      */
@@ -314,6 +358,15 @@
         return this;
     }
 
+    /**
+     * UID of the package that is captured if selected.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ContentRecordingSession setTargetUid( int value) {
+        mTargetUid = value;
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -325,7 +378,8 @@
                 "contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
                 "displayToRecord = " + mDisplayToRecord + ", " +
                 "tokenToRecord = " + mTokenToRecord + ", " +
-                "waitingForConsent = " + mWaitingForConsent +
+                "waitingForConsent = " + mWaitingForConsent + ", " +
+                "targetUid = " + mTargetUid +
         " }";
     }
 
@@ -346,7 +400,8 @@
                 && mContentToRecord == that.mContentToRecord
                 && mDisplayToRecord == that.mDisplayToRecord
                 && java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
-                && mWaitingForConsent == that.mWaitingForConsent;
+                && mWaitingForConsent == that.mWaitingForConsent
+                && mTargetUid == that.mTargetUid;
     }
 
     @Override
@@ -361,6 +416,7 @@
         _hash = 31 * _hash + mDisplayToRecord;
         _hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
         _hash = 31 * _hash + Boolean.hashCode(mWaitingForConsent);
+        _hash = 31 * _hash + mTargetUid;
         return _hash;
     }
 
@@ -378,6 +434,7 @@
         dest.writeInt(mContentToRecord);
         dest.writeInt(mDisplayToRecord);
         if (mTokenToRecord != null) dest.writeStrongBinder(mTokenToRecord);
+        dest.writeInt(mTargetUid);
     }
 
     @Override
@@ -397,6 +454,7 @@
         int contentToRecord = in.readInt();
         int displayToRecord = in.readInt();
         IBinder tokenToRecord = (flg & 0x8) == 0 ? null : (IBinder) in.readStrongBinder();
+        int targetUid = in.readInt();
 
         this.mVirtualDisplayId = virtualDisplayId;
         this.mContentToRecord = contentToRecord;
@@ -412,6 +470,7 @@
         this.mDisplayToRecord = displayToRecord;
         this.mTokenToRecord = tokenToRecord;
         this.mWaitingForConsent = waitingForConsent;
+        this.mTargetUid = targetUid;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -442,6 +501,7 @@
         private int mDisplayToRecord;
         private @Nullable IBinder mTokenToRecord;
         private boolean mWaitingForConsent;
+        private int mTargetUid;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -513,10 +573,21 @@
             return this;
         }
 
+        /**
+         * UID of the package that is captured if selected.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTargetUid(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mTargetUid = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull ContentRecordingSession build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20; // Mark builder used
+            mBuilderFieldsSet |= 0x40; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mVirtualDisplayId = INVALID_DISPLAY;
@@ -533,17 +604,21 @@
             if ((mBuilderFieldsSet & 0x10) == 0) {
                 mWaitingForConsent = false;
             }
+            if ((mBuilderFieldsSet & 0x20) == 0) {
+                mTargetUid = TARGET_UID_UNKNOWN;
+            }
             ContentRecordingSession o = new ContentRecordingSession(
                     mVirtualDisplayId,
                     mContentToRecord,
                     mDisplayToRecord,
                     mTokenToRecord,
-                    mWaitingForConsent);
+                    mWaitingForConsent,
+                    mTargetUid);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x20) != 0) {
+            if ((mBuilderFieldsSet & 0x40) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -551,10 +626,10 @@
     }
 
     @DataClass.Generated(
-            time = 1683628463074L,
+            time = 1697456140720L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
-            inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\nprivate  int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate  int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate  boolean mWaitingForConsent\npublic static  android.view.ContentRecordingSession createDisplaySession(int)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\npublic static final  int TARGET_UID_FULL_SCREEN\npublic static final  int TARGET_UID_UNKNOWN\nprivate  int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate  int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate  boolean mWaitingForConsent\nprivate  int mTargetUid\npublic static  android.view.ContentRecordingSession createDisplaySession(int)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0b2b6ce..5069455 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -26,6 +26,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.app.KeyguardManager;
 import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -1366,7 +1367,8 @@
             // form of the larger DISPLAY_CHANGED event
             mGlobal.registerDisplayListener(toRegister, executor,
                     DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED
-                            | DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+                            | DisplayManagerGlobal.EVENT_DISPLAY_CHANGED,
+                    ActivityThread.currentPackageName());
         }
 
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 16318e0..e9d0e4c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9308,6 +9308,7 @@
                 structure.setAutofillType(autofillType);
                 structure.setAutofillHints(getAutofillHints());
                 structure.setAutofillValue(getAutofillValue());
+                structure.setIsCredential(isCredential());
             }
             structure.setImportantForAutofill(getImportantForAutofill());
             structure.setReceiveContentMimeTypes(getReceiveContentMimeTypes());
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b5648cc..9392783 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1549,7 +1549,8 @@
                         mHandler,
                         DisplayManager.EVENT_FLAG_DISPLAY_ADDED
                         | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
-                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED,
+                        mBasePackageName);
     }
 
     /**
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 2c2ae06..bb2c7c8 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -397,6 +397,13 @@
     public void setImportantForAutofill(@AutofillImportance int mode) {}
 
     /**
+     * Sets whether the node is a credential. See {@link View#isCredential}.
+     *
+     * @hide
+     */
+    public void setIsCredential(boolean isCredential) {}
+
+    /**
      * Sets the MIME types accepted by this view. See {@link View#getReceiveContentMimeTypes()}.
      *
      * <p>Should only be set when the node is used for Autofill or Content Capture purposes - it
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a3b93b4..4f03ce9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
+import static android.view.flags.Flags.FLAG_WM_DISPLAY_REFRESH_RATE_TEST;
 import static android.view.View.STATUS_BAR_DISABLE_BACK;
 import static android.view.View.STATUS_BAR_DISABLE_CLOCK;
 import static android.view.View.STATUS_BAR_DISABLE_EXPAND;
@@ -3905,6 +3906,7 @@
          * This value is ignored if {@link #preferredDisplayModeId} is set.
          * @hide
          */
+        @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
         @TestApi
         public float preferredMinDisplayRefreshRate;
 
@@ -3914,6 +3916,7 @@
          * This value is ignored if {@link #preferredDisplayModeId} is set.
          * @hide
          */
+        @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
         @TestApi
         public float preferredMaxDisplayRefreshRate;
 
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 60f46e6..12ce0f4 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -1731,6 +1731,9 @@
     @Override
     public void sendAttachOverlayResult(
             @AccessibilityService.AttachOverlayResult int result, int interactionId) {
+        if (!Flags.a11yOverlayCallbacks()) {
+            return;
+        }
         synchronized (mInstanceLock) {
             if (mAttachAccessibilityOverlayCallbacks.contains(interactionId)) {
                 final Pair<Executor, IntConsumer> pair =
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 09b2f9c..fa0052c 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -99,7 +99,6 @@
     /** @hide */
     public static final int UNDEFINED_CONNECTION_ID = -1;
     /** @hide */
-    @TestApi
     public static final int UNDEFINED_WINDOW_ID = -1;
     /** @hide */
     public static final int ANY_WINDOW_ID = -2;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 85dadd4..cc612ed 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -13,3 +13,10 @@
     description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
     bug: "303871725"
 }
+
+flag {
+        name: "a11y_overlay_callbacks"
+    namespace: "accessibility"
+    description: "Whether to allow the passing of result callbacks when attaching a11y overlays."
+    bug: "304478691"
+}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 2241fd5..b44d6a4 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -317,16 +317,14 @@
             }
         }
 
-        // Should not be possible for mComponentName to be null here but check anyway
-        if (mManager.mOptions.contentProtectionOptions.enableReceiver
-                && mManager.getContentProtectionEventBuffer() != null
-                && mComponentName != null) {
+        if (isContentProtectionEnabled()) {
             mContentProtectionEventProcessor =
                     new ContentProtectionEventProcessor(
                             mManager.getContentProtectionEventBuffer(),
                             mHandler,
                             mSystemServerInterface,
-                            mComponentName.getPackageName());
+                            mComponentName.getPackageName(),
+                            mManager.mOptions.contentProtectionOptions);
         } else {
             mContentProtectionEventProcessor = null;
         }
@@ -956,4 +954,15 @@
     private boolean isContentCaptureReceiverEnabled() {
         return mManager.mOptions.enableReceiver;
     }
+
+    @UiThread
+    private boolean isContentProtectionEnabled() {
+        // Should not be possible for mComponentName to be null here but check anyway
+        // Should not be possible for groups to be empty if receiver is enabled but check anyway
+        return mManager.mOptions.contentProtectionOptions.enableReceiver
+                && mManager.getContentProtectionEventBuffer() != null
+                && mComponentName != null
+                && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
+                        || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
+    }
 }
diff --git a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
index b44abf3..aaf90bd 100644
--- a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
+++ b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
@@ -19,9 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiThread;
+import android.content.ContentCaptureOptions;
 import android.content.pm.ParceledListSlice;
 import android.os.Handler;
-import android.text.InputType;
 import android.util.Log;
 import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.IContentCaptureManager;
@@ -33,10 +33,10 @@
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Stream;
 
 /**
  * Main entry point for processing {@link ContentCaptureEvent} for the content protection flow.
@@ -47,33 +47,13 @@
 
     private static final String TAG = "ContentProtectionEventProcessor";
 
-    private static final List<Integer> PASSWORD_FIELD_INPUT_TYPES =
-            Collections.unmodifiableList(
-                    Arrays.asList(
-                            InputType.TYPE_NUMBER_VARIATION_PASSWORD,
-                            InputType.TYPE_TEXT_VARIATION_PASSWORD,
-                            InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
-                            InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD));
-
-    private static final List<String> PASSWORD_TEXTS =
-            Collections.unmodifiableList(
-                    Arrays.asList("password", "pass word", "code", "pin", "credential"));
-
-    private static final List<String> ADDITIONAL_SUSPICIOUS_TEXTS =
-            Collections.unmodifiableList(
-                    Arrays.asList("user", "mail", "phone", "number", "login", "log in", "sign in"));
-
     private static final Duration MIN_DURATION_BETWEEN_FLUSHING = Duration.ofSeconds(3);
 
-    private static final String ANDROID_CLASS_NAME_PREFIX = "android.";
-
     private static final Set<Integer> EVENT_TYPES_TO_STORE =
-            Collections.unmodifiableSet(
-                    new HashSet<>(
-                            Arrays.asList(
-                                    ContentCaptureEvent.TYPE_VIEW_APPEARED,
-                                    ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
-                                    ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED)));
+            Set.of(
+                    ContentCaptureEvent.TYPE_VIEW_APPEARED,
+                    ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
+                    ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED);
 
     private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150;
 
@@ -85,11 +65,7 @@
 
     @NonNull private final String mPackageName;
 
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public boolean mPasswordFieldDetected = false;
-
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public boolean mSuspiciousTextDetected = false;
+    @NonNull private final ContentCaptureOptions.ContentProtectionOptions mOptions;
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     @Nullable
@@ -97,15 +73,32 @@
 
     private int mResetLoginRemainingEventsToProcess;
 
+    private boolean mAnyGroupFound = false;
+
+    // Ordered by priority
+    private final List<SearchGroup> mGroupsRequired;
+
+    // Ordered by priority
+    private final List<SearchGroup> mGroupsOptional;
+
+    // Ordered by priority
+    private final List<SearchGroup> mGroupsAll;
+
     public ContentProtectionEventProcessor(
             @NonNull RingBuffer<ContentCaptureEvent> eventBuffer,
             @NonNull Handler handler,
             @NonNull IContentCaptureManager contentCaptureManager,
-            @NonNull String packageName) {
+            @NonNull String packageName,
+            @NonNull ContentCaptureOptions.ContentProtectionOptions options) {
         mEventBuffer = eventBuffer;
         mHandler = handler;
         mContentCaptureManager = contentCaptureManager;
         mPackageName = packageName;
+        mOptions = options;
+        mGroupsRequired = options.requiredGroups.stream().map(SearchGroup::new).toList();
+        mGroupsOptional = options.optionalGroups.stream().map(SearchGroup::new).toList();
+        mGroupsAll =
+                Stream.of(mGroupsRequired, mGroupsOptional).flatMap(Collection::stream).toList();
     }
 
     /** Main entry point for {@link ContentCaptureEvent} processing. */
@@ -130,9 +123,31 @@
 
     @UiThread
     private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) {
-        mPasswordFieldDetected |= isPasswordField(event);
-        mSuspiciousTextDetected |= isSuspiciousText(event);
-        if (mPasswordFieldDetected && mSuspiciousTextDetected) {
+        ViewNode viewNode = event.getViewNode();
+        String eventText = ContentProtectionUtils.getEventTextLower(event);
+        String viewNodeText = ContentProtectionUtils.getViewNodeTextLower(viewNode);
+        String hintText = ContentProtectionUtils.getHintTextLower(viewNode);
+
+        mGroupsAll.stream()
+                .filter(group -> !group.mFound)
+                .filter(
+                        group ->
+                                group.matches(eventText)
+                                        || group.matches(viewNodeText)
+                                        || group.matches(hintText))
+                .findFirst()
+                .ifPresent(
+                        group -> {
+                            group.mFound = true;
+                            mAnyGroupFound = true;
+                        });
+
+        boolean loginDetected =
+                mGroupsRequired.stream().allMatch(group -> group.mFound)
+                        && mGroupsOptional.stream().filter(group -> group.mFound).count()
+                                >= mOptions.optionalGroupsThreshold;
+
+        if (loginDetected) {
             loginDetected();
         } else {
             maybeResetLoginFlags();
@@ -150,14 +165,13 @@
 
     @UiThread
     private void resetLoginFlags() {
-        mPasswordFieldDetected = false;
-        mSuspiciousTextDetected = false;
-        mResetLoginRemainingEventsToProcess = 0;
+        mGroupsAll.forEach(group -> group.mFound = false);
+        mAnyGroupFound = false;
     }
 
     @UiThread
     private void maybeResetLoginFlags() {
-        if (mPasswordFieldDetected || mSuspiciousTextDetected) {
+        if (mAnyGroupFound) {
             if (mResetLoginRemainingEventsToProcess <= 0) {
                 mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS;
             } else {
@@ -194,61 +208,21 @@
         }
     }
 
-    private boolean isPasswordField(@NonNull ContentCaptureEvent event) {
-        return isPasswordField(event.getViewNode());
-    }
+    private static final class SearchGroup {
 
-    private boolean isPasswordField(@Nullable ViewNode viewNode) {
-        if (viewNode == null) {
-            return false;
+        @NonNull private final List<String> mSearchStrings;
+
+        public boolean mFound = false;
+
+        SearchGroup(@NonNull List<String> searchStrings) {
+            mSearchStrings = searchStrings;
         }
-        return isAndroidPasswordField(viewNode) || isWebViewPasswordField(viewNode);
-    }
 
-    private boolean isAndroidPasswordField(@NonNull ViewNode viewNode) {
-        if (!isAndroidViewNode(viewNode)) {
-            return false;
+        public boolean matches(@Nullable String text) {
+            if (text == null) {
+                return false;
+            }
+            return mSearchStrings.stream().anyMatch(text::contains);
         }
-        int inputType = viewNode.getInputType();
-        return PASSWORD_FIELD_INPUT_TYPES.stream()
-                .anyMatch(passwordInputType -> (inputType & passwordInputType) != 0);
-    }
-
-    private boolean isWebViewPasswordField(@NonNull ViewNode viewNode) {
-        if (viewNode.getClassName() != null) {
-            return false;
-        }
-        return isPasswordText(ContentProtectionUtils.getViewNodeText(viewNode));
-    }
-
-    private boolean isAndroidViewNode(@NonNull ViewNode viewNode) {
-        String className = viewNode.getClassName();
-        return className != null && className.startsWith(ANDROID_CLASS_NAME_PREFIX);
-    }
-
-    private boolean isSuspiciousText(@NonNull ContentCaptureEvent event) {
-        return isSuspiciousText(ContentProtectionUtils.getEventText(event))
-                || isSuspiciousText(ContentProtectionUtils.getViewNodeText(event));
-    }
-
-    private boolean isSuspiciousText(@Nullable String text) {
-        if (text == null) {
-            return false;
-        }
-        if (isPasswordText(text)) {
-            return true;
-        }
-        String lowerCaseText = text.toLowerCase();
-        return ADDITIONAL_SUSPICIOUS_TEXTS.stream()
-                .anyMatch(suspiciousText -> lowerCaseText.contains(suspiciousText));
-    }
-
-    private boolean isPasswordText(@Nullable String text) {
-        if (text == null) {
-            return false;
-        }
-        String lowerCaseText = text.toLowerCase();
-        return PASSWORD_TEXTS.stream()
-                .anyMatch(passwordText -> lowerCaseText.contains(passwordText));
     }
 }
diff --git a/core/java/android/view/contentprotection/ContentProtectionUtils.java b/core/java/android/view/contentprotection/ContentProtectionUtils.java
index 9abf6f1..1ecac7f 100644
--- a/core/java/android/view/contentprotection/ContentProtectionUtils.java
+++ b/core/java/android/view/contentprotection/ContentProtectionUtils.java
@@ -28,33 +28,39 @@
  */
 public final class ContentProtectionUtils {
 
-    /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */
+    /** Returns the lowercase text extracted from the {@link ContentCaptureEvent}, if set. */
     @Nullable
-    public static String getEventText(@NonNull ContentCaptureEvent event) {
+    public static String getEventTextLower(@NonNull ContentCaptureEvent event) {
         CharSequence text = event.getText();
         if (text == null) {
             return null;
         }
-        return text.toString();
+        return text.toString().toLowerCase();
     }
 
-    /** Returns the text extracted from the event's {@link ViewNode}, if set. */
+    /** Returns the lowercase text extracted from the {@link ViewNode}, if set. */
     @Nullable
-    public static String getViewNodeText(@NonNull ContentCaptureEvent event) {
-        ViewNode viewNode = event.getViewNode();
+    public static String getViewNodeTextLower(@Nullable ViewNode viewNode) {
         if (viewNode == null) {
             return null;
         }
-        return getViewNodeText(viewNode);
-    }
-
-    /** Returns the text extracted directly from the {@link ViewNode}, if set. */
-    @Nullable
-    public static String getViewNodeText(@NonNull ViewNode viewNode) {
         CharSequence text = viewNode.getText();
         if (text == null) {
             return null;
         }
-        return text.toString();
+        return text.toString().toLowerCase();
+    }
+
+    /** Returns the lowercase hint text extracted from the {@link ViewNode}, if set. */
+    @Nullable
+    public static String getHintTextLower(@Nullable ViewNode viewNode) {
+        if (viewNode == null) {
+            return null;
+        }
+        String text = viewNode.getHint();
+        if (text == null) {
+            return null;
+        }
+        return text.toLowerCase();
     }
 }
diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index 56b5fac..fd96890 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -26,4 +26,11 @@
   namespace: "core_graphics"
   description: "Enable the `setFrameRate` callback"
   bug: "299946220"
+}
+
+flag {
+    name: "wm_display_refresh_rate_test"
+    namespace: "core_graphics"
+    description: "Adds WindowManager display refresh rate fields to test API"
+    bug: "304475199"
 }
\ No newline at end of file
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 5d14698..eb9d62b 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -263,6 +263,7 @@
             Trace.instantForTrack(Trace.TRACE_TAG_VIEW, mTrackName, "markSyncReady");
         }
         synchronized (mLock) {
+            toggleTimeout(false);
             if (mHasWMSync) {
                 try {
                     WindowManagerGlobal.getWindowManagerService().markSurfaceSyncGroupReady(mToken);
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 43fa0be..4e0f9a5 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -88,6 +88,26 @@
      */
     public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
 
+    /**
+     * Reorders the TaskFragment to be the bottom-most in the Task. Note that this op will bring the
+     * TaskFragment to the bottom of the Task below all the other Activities and TaskFragments.
+     *
+     * This is only allowed for system organizers. See
+     * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
+     * ITaskFragmentOrganizer, boolean)}
+     */
+    public static final int OP_TYPE_REORDER_TO_BOTTOM_OF_TASK = 12;
+
+    /**
+     * Reorders the TaskFragment to be the top-most in the Task. Note that this op will bring the
+     * TaskFragment to the top of the Task above all the other Activities and TaskFragments.
+     *
+     * This is only allowed for system organizers. See
+     * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
+     * ITaskFragmentOrganizer, boolean)}
+     */
+    public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;
+
     @IntDef(prefix = { "OP_TYPE_" }, value = {
             OP_TYPE_UNKNOWN,
             OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -101,7 +121,9 @@
             OP_TYPE_SET_ANIMATION_PARAMS,
             OP_TYPE_SET_RELATIVE_BOUNDS,
             OP_TYPE_REORDER_TO_FRONT,
-            OP_TYPE_SET_ISOLATED_NAVIGATION
+            OP_TYPE_SET_ISOLATED_NAVIGATION,
+            OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
+            OP_TYPE_REORDER_TO_TOP_OF_TASK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OperationType {}
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index 72a1bac..ca6c54d 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -24,6 +24,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Handler;
@@ -147,7 +148,8 @@
                 public void registerDisplayListener(DisplayManager.DisplayListener listener) {
                     manager.registerDisplayListener(listener, handler,
                             DisplayManager.EVENT_FLAG_DISPLAY_ADDED
-                                    | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+                                    | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED,
+                            ActivityThread.currentPackageName());
                 }
 
                 @Override
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index eb14db0..068f4dd 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -115,6 +115,9 @@
 
                 // Only set if the app defined a monochrome icon.
                 optional string monochrome_icon_bitmap_path = 3;
+
+                // The component name of the original activity (pre-archival).
+                optional string original_component_name = 4;
             }
 
             /** Information about main activities. */
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 149f58f..c2e6b60c 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -42,8 +42,8 @@
 public class DisplayManagerGlobalTest {
 
     private static final long ALL_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
-                    | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
-                    | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+            | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+            | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
 
     @Mock
     private IDisplayManager mDisplayManager;
@@ -69,7 +69,8 @@
 
     @Test
     public void testDisplayListenerIsCalled_WhenDisplayEventOccurs() throws RemoteException {
-        mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS);
+        mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS,
+                null);
         Mockito.verify(mDisplayManager)
                 .registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
         IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -97,26 +98,27 @@
     public void testDisplayListenerIsNotCalled_WhenClientIsNotSubscribed() throws RemoteException {
         // First we subscribe to all events in order to test that the subsequent calls to
         // registerDisplayListener will update the event mask.
-        mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS);
+        mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS,
+                null);
         Mockito.verify(mDisplayManager)
                 .registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
         IDisplayManagerCallback callback = mCallbackCaptor.getValue();
 
         int displayId = 1;
         mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
-                ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED);
+                ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED, null);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
         waitForHandler();
         Mockito.verifyZeroInteractions(mListener);
 
         mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
-                ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+                ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
         waitForHandler();
         Mockito.verifyZeroInteractions(mListener);
 
         mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
-                ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+                ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, null);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
         waitForHandler();
         Mockito.verifyZeroInteractions(mListener);
@@ -139,7 +141,7 @@
     public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
             throws RemoteException {
         mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
-                DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+                DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS, null);
         InOrder inOrder = Mockito.inOrder(mDisplayManager);
 
         inOrder.verify(mDisplayManager)
@@ -163,6 +165,7 @@
     }
 
     private void waitForHandler() {
-        mHandler.runWithScissors(() -> { }, 0);
+        mHandler.runWithScissors(() -> {
+        }, 0);
     }
 }
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index e76d266..d47d789 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -115,6 +115,26 @@
                         new ContentCaptureOptions.ContentProtectionOptions(
                                 /* enableReceiver= */ true,
                                 -BUFFER_SIZE,
+                                /* requiredGroups= */ List.of(List.of("a")),
+                                /* optionalGroups= */ Collections.emptyList(),
+                                /* optionalGroupsThreshold= */ 0));
+        MainContentCaptureSession session = createSession(options);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+    }
+
+    @Test
+    public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        new ContentCaptureOptions.ContentProtectionOptions(
+                                /* enableReceiver= */ true,
+                                BUFFER_SIZE,
                                 /* requiredGroups= */ Collections.emptyList(),
                                 /* optionalGroups= */ Collections.emptyList(),
                                 /* optionalGroupsThreshold= */ 0));
@@ -320,7 +340,7 @@
                 new ContentCaptureOptions.ContentProtectionOptions(
                         enableContentProtectionReceiver,
                         BUFFER_SIZE,
-                        /* requiredGroups= */ Collections.emptyList(),
+                        /* requiredGroups= */ List.of(List.of("a")),
                         /* optionalGroups= */ Collections.emptyList(),
                         /* optionalGroupsThreshold= */ 0));
     }
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index 39a2e0e..ba0dbf4 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -29,12 +29,13 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentCaptureOptions;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.Handler;
-import android.os.Looper;
-import android.text.InputType;
+import android.os.test.TestLooper;
 import android.view.View;
 import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.IContentCaptureManager;
@@ -57,6 +58,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.mockito.verification.VerificationMode;
 
 import java.time.Instant;
 import java.util.ArrayList;
@@ -75,13 +77,25 @@
 
     private static final String PACKAGE_NAME = "com.test.package.name";
 
-    private static final String ANDROID_CLASS_NAME = "android.test.some.class.name";
+    private static final String TEXT_REQUIRED1 = "TEXT REQUIRED1 TEXT";
 
-    private static final String PASSWORD_TEXT = "ENTER PASSWORD HERE";
+    private static final String TEXT_REQUIRED2 = "TEXT REQUIRED2 TEXT";
 
-    private static final String SUSPICIOUS_TEXT = "PLEASE SIGN IN";
+    private static final String TEXT_OPTIONAL1 = "TEXT OPTIONAL1 TEXT";
 
-    private static final String SAFE_TEXT = "SAFE TEXT";
+    private static final String TEXT_OPTIONAL2 = "TEXT OPTIONAL2 TEXT";
+
+    private static final String TEXT_CONTAINS_OPTIONAL3 = "TEXTOPTIONAL3TEXT";
+
+    private static final String TEXT_SHARED = "TEXT SHARED TEXT";
+
+    private static final String TEXT_SAFE = "TEXT SAFE TEXT";
+
+    private static final List<List<String>> REQUIRED_GROUPS =
+            List.of(List.of("required1", "missing"), List.of("required2", "shared"));
+
+    private static final List<List<String>> OPTIONAL_GROUPS =
+            List.of(List.of("optional1"), List.of("optional2", "optional3"), List.of("shared"));
 
     private static final ContentCaptureEvent PROCESS_EVENT = createProcessEvent();
 
@@ -91,7 +105,17 @@
     private static final Set<Integer> EVENT_TYPES_TO_STORE =
             ImmutableSet.of(TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED);
 
-    private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150;
+    private static final int BUFFER_SIZE = 150;
+
+    private static final int OPTIONAL_GROUPS_THRESHOLD = 1;
+
+    private static final ContentCaptureOptions.ContentProtectionOptions OPTIONS =
+            new ContentCaptureOptions.ContentProtectionOptions(
+                    /* enableReceiver= */ true,
+                    BUFFER_SIZE,
+                    REQUIRED_GROUPS,
+                    OPTIONAL_GROUPS,
+                    OPTIONAL_GROUPS_THRESHOLD);
 
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -101,16 +125,19 @@
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
-    private ContentProtectionEventProcessor mContentProtectionEventProcessor;
+    private final TestLooper mTestLooper = new TestLooper();
+
+    @NonNull private ContentProtectionEventProcessor mContentProtectionEventProcessor;
 
     @Before
     public void setup() {
         mContentProtectionEventProcessor =
                 new ContentProtectionEventProcessor(
                         mMockEventBuffer,
-                        new Handler(Looper.getMainLooper()),
+                        new Handler(mTestLooper.getLooper()),
                         mMockContentCaptureManager,
-                        PACKAGE_NAME);
+                        PACKAGE_NAME,
+                        OPTIONS);
     }
 
     @Test
@@ -156,347 +183,224 @@
     }
 
     @Test
-    public void processEvent_loginDetected_inspectsOnlyTypeViewAppeared() {
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
+    public void processEvent_loginDetected_true_eventText() throws Exception {
+        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ TEXT_REQUIRED1,
+                        /* viewNodeText= */ null,
+                        /* hintText= */ null));
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ TEXT_REQUIRED2,
+                        /* viewNodeText= */ null,
+                        /* hintText= */ null));
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ TEXT_OPTIONAL1,
+                        /* viewNodeText= */ null,
+                        /* hintText= */ null));
+
+        assertLoginDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_true_viewNodeText() throws Exception {
+        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ null,
+                        /* viewNodeText= */ TEXT_REQUIRED1,
+                        /* hintText= */ null));
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ null,
+                        /* viewNodeText= */ TEXT_REQUIRED2,
+                        /* hintText= */ null));
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ null,
+                        /* viewNodeText= */ TEXT_OPTIONAL1,
+                        /* hintText= */ null));
+
+        assertLoginDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_true_hintText() throws Exception {
+        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ null,
+                        /* viewNodeText= */ null,
+                        /* hintText= */ TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ null,
+                        /* viewNodeText= */ null,
+                        /* hintText= */ TEXT_REQUIRED2));
+        mContentProtectionEventProcessor.processEvent(
+                createProcessEvent(
+                        /* eventText= */ null,
+                        /* viewNodeText= */ null,
+                        /* hintText= */ TEXT_OPTIONAL1));
+
+        assertLoginDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_true_differentOptionalGroup() throws Exception {
+        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL2));
+
+        assertLoginDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_true_usesContains() throws Exception {
+        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_CONTAINS_OPTIONAL3));
+
+        assertLoginDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_false_missingRequiredGroups() {
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+
+        assertLoginNotDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_false_missingOptionalGroups() {
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+
+        assertLoginNotDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_false_safeText() {
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+
+        assertLoginNotDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_false_sharedTextOnce() {
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+
+        assertLoginNotDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_true_sharedTextMultiple() throws Exception {
+        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
+
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+
+        assertLoginDetected();
+    }
+
+    @Test
+    public void processEvent_loginDetected_false_inspectsOnlyTypeViewAppeared() {
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
 
         for (int type = -100; type <= 100; type++) {
             if (type == TYPE_VIEW_APPEARED) {
                 continue;
             }
-
-            mContentProtectionEventProcessor.processEvent(createEvent(type));
-
-            assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
-            assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
+            ContentCaptureEvent event = createEvent(type);
+            event.setText(TEXT_OPTIONAL1);
+            mContentProtectionEventProcessor.processEvent(event);
         }
 
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
+        assertLoginNotDetected();
     }
 
     @Test
-    public void processEvent_loginDetected() throws Exception {
+    public void processEvent_loginDetected_true_belowResetLimit() throws Exception {
         when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
 
-        mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
 
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer).clear();
-        verify(mMockEventBuffer).toArray();
-        assertOnLoginDetected();
-    }
-
-    @Test
-    public void processEvent_loginDetected_passwordFieldNotDetected() {
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-
-        mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void processEvent_loginDetected_suspiciousTextNotDetected() {
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-
-        mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void processEvent_loginDetected_withoutViewNode() {
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-
-        mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void processEvent_loginDetected_belowResetLimit() throws Exception {
-        when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
-        ContentCaptureEvent event =
-                createAndroidPasswordFieldEvent(
-                        ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
-        for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; i++) {
+        for (int i = 0; i < BUFFER_SIZE - 2; i++) {
             mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
         }
 
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
+        assertLoginNotDetected();
 
-        mContentProtectionEventProcessor.processEvent(event);
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
 
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer).clear();
-        verify(mMockEventBuffer).toArray();
-        assertOnLoginDetected();
+        assertLoginDetected();
     }
 
     @Test
-    public void processEvent_loginDetected_aboveResetLimit() throws Exception {
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
-        ContentCaptureEvent event =
-                createAndroidPasswordFieldEvent(
-                        ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
+    public void processEvent_loginDetected_false_aboveResetLimit() {
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
 
-        for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS + 1; i++) {
+        for (int i = 0; i < BUFFER_SIZE - 1; i++) {
             mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
         }
 
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
+        assertLoginNotDetected();
 
-        mContentProtectionEventProcessor.processEvent(event);
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
 
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
+        assertLoginNotDetected();
     }
 
     @Test
     public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception {
         when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
 
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
-        mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+        for (int i = 0; i < 2; i++) {
+            mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+            mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+            mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+            mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+        }
 
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
-        mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer).clear();
-        verify(mMockEventBuffer).toArray();
-        assertOnLoginDetected();
+        assertLoginDetected();
     }
 
     @Test
     public void processEvent_multipleLoginsDetected_aboveFlushThreshold() throws Exception {
         when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
 
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
         mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
 
         mContentProtectionEventProcessor.mLastFlushTime = Instant.now().minusSeconds(5);
 
-        mContentProtectionEventProcessor.mPasswordFieldDetected = true;
-        mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+        mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
         mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
 
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer, times(2)).clear();
-        verify(mMockEventBuffer, times(2)).toArray();
-        assertOnLoginDetected(PROCESS_EVENT, /* times= */ 2);
-    }
-
-    @Test
-    public void isPasswordField_android() {
-        ContentCaptureEvent event =
-                createAndroidPasswordFieldEvent(
-                        ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isPasswordField_android_withoutClassName() {
-        ContentCaptureEvent event =
-                createAndroidPasswordFieldEvent(
-                        /* className= */ null, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isPasswordField_android_wrongClassName() {
-        ContentCaptureEvent event =
-                createAndroidPasswordFieldEvent(
-                        "wrong.prefix" + ANDROID_CLASS_NAME,
-                        InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isPasswordField_android_wrongInputType() {
-        ContentCaptureEvent event =
-                createAndroidPasswordFieldEvent(
-                        ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_NORMAL);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isPasswordField_webView() throws Exception {
-        ContentCaptureEvent event =
-                createWebViewPasswordFieldEvent(
-                        /* className= */ null, /* eventText= */ null, PASSWORD_TEXT);
-        when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[] {event});
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer).clear();
-        verify(mMockEventBuffer).toArray();
-        assertOnLoginDetected(event, /* times= */ 1);
-    }
-
-    @Test
-    public void isPasswordField_webView_withClassName() {
-        ContentCaptureEvent event =
-                createWebViewPasswordFieldEvent(
-                        /* className= */ "any.class.name", /* eventText= */ null, PASSWORD_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isPasswordField_webView_withSafeViewNodeText() {
-        ContentCaptureEvent event =
-                createWebViewPasswordFieldEvent(
-                        /* className= */ null, /* eventText= */ null, SAFE_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isPasswordField_webView_withEventText() {
-        ContentCaptureEvent event =
-                createWebViewPasswordFieldEvent(/* className= */ null, PASSWORD_TEXT, SAFE_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isSuspiciousText_withSafeText() {
-        ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SAFE_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isSuspiciousText_eventText_suspiciousText() {
-        ContentCaptureEvent event = createSuspiciousTextEvent(SUSPICIOUS_TEXT, SAFE_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isSuspiciousText_viewNodeText_suspiciousText() {
-        ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SUSPICIOUS_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isSuspiciousText_eventText_passwordText() {
-        ContentCaptureEvent event = createSuspiciousTextEvent(PASSWORD_TEXT, SAFE_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
-    }
-
-    @Test
-    public void isSuspiciousText_viewNodeText_passwordText() {
-        // Specify the class to differ from {@link isPasswordField_webView} test in this version
-        ContentCaptureEvent event =
-                createProcessEvent(
-                        "test.class.not.a.web.view", /* inputType= */ 0, SAFE_TEXT, PASSWORD_TEXT);
-
-        mContentProtectionEventProcessor.processEvent(event);
-
-        assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
-        verify(mMockEventBuffer, never()).clear();
-        verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
+        assertLoginDetected(times(2));
     }
 
     private static ContentCaptureEvent createEvent(int type) {
@@ -511,20 +415,20 @@
         return createEvent(TYPE_VIEW_APPEARED);
     }
 
+    private ContentCaptureEvent createProcessEvent(@Nullable String eventText) {
+        return createProcessEvent(eventText, /* viewNodeText= */ null, /* hintText= */ null);
+    }
+
     private ContentCaptureEvent createProcessEvent(
-            @Nullable String className,
-            int inputType,
-            @Nullable String eventText,
-            @Nullable String viewNodeText) {
+            @Nullable String eventText, @Nullable String viewNodeText, @Nullable String hintText) {
         View view = new View(mContext);
         ViewStructureImpl viewStructure = new ViewStructureImpl(view);
-        if (className != null) {
-            viewStructure.setClassName(className);
-        }
         if (viewNodeText != null) {
             viewStructure.setText(viewNodeText);
         }
-        viewStructure.setInputType(inputType);
+        if (hintText != null) {
+            viewStructure.setHint(hintText);
+        }
 
         ContentCaptureEvent event = createProcessEvent();
         event.setViewNode(viewStructure.getNode());
@@ -535,34 +439,28 @@
         return event;
     }
 
-    private ContentCaptureEvent createAndroidPasswordFieldEvent(
-            @Nullable String className, int inputType) {
-        return createProcessEvent(
-                className, inputType, /* eventText= */ null, /* viewNodeText= */ null);
+    private void assertLoginNotDetected() {
+        mTestLooper.dispatchAll();
+        verify(mMockEventBuffer, never()).clear();
+        verify(mMockEventBuffer, never()).toArray();
+        verifyZeroInteractions(mMockContentCaptureManager);
     }
 
-    private ContentCaptureEvent createWebViewPasswordFieldEvent(
-            @Nullable String className, @Nullable String eventText, @Nullable String viewNodeText) {
-        return createProcessEvent(className, /* inputType= */ 0, eventText, viewNodeText);
+    private void assertLoginDetected() throws Exception {
+        assertLoginDetected(times(1));
     }
 
-    private ContentCaptureEvent createSuspiciousTextEvent(
-            @Nullable String eventText, @Nullable String viewNodeText) {
-        return createProcessEvent(
-                /* className= */ null, /* inputType= */ 0, eventText, viewNodeText);
-    }
+    private void assertLoginDetected(@NonNull VerificationMode verificationMode) throws Exception {
+        mTestLooper.dispatchAll();
+        verify(mMockEventBuffer, verificationMode).clear();
+        verify(mMockEventBuffer, verificationMode).toArray();
 
-    private void assertOnLoginDetected() throws Exception {
-        assertOnLoginDetected(PROCESS_EVENT, /* times= */ 1);
-    }
-
-    private void assertOnLoginDetected(ContentCaptureEvent event, int times) throws Exception {
         ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
-        verify(mMockContentCaptureManager, times(times)).onLoginDetected(captor.capture());
+        verify(mMockContentCaptureManager, verificationMode).onLoginDetected(captor.capture());
 
         assertThat(captor.getValue()).isNotNull();
         List<ContentCaptureEvent> actual = captor.getValue().getList();
         assertThat(actual).isNotNull();
-        assertThat(actual).containsExactly(event);
+        assertThat(actual).containsExactly(PROCESS_EVENT);
     }
 }
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
index 1459799..fbe478e 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 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.
@@ -44,68 +44,74 @@
 
     private static final String TEXT = "TEST_TEXT";
 
-    private static final ContentCaptureEvent EVENT = createEvent();
-
-    private static final ViewNode VIEW_NODE = new ViewNode();
-
-    private static final ViewNode VIEW_NODE_WITH_TEXT = createViewNodeWithText();
+    private static final String TEXT_LOWER = TEXT.toLowerCase();
 
     @Test
-    public void event_getEventText_null() {
-        String actual = ContentProtectionUtils.getEventText(EVENT);
+    public void getEventTextLower_null() {
+        String actual = ContentProtectionUtils.getEventTextLower(createEvent());
 
         assertThat(actual).isNull();
     }
 
     @Test
-    public void event_getEventText_notNull() {
-        ContentCaptureEvent event = createEvent();
-        event.setText(TEXT);
+    public void getEventTextLower_notNull() {
+        String actual = ContentProtectionUtils.getEventTextLower(createEventWithText());
 
-        String actual = ContentProtectionUtils.getEventText(event);
-
-        assertThat(actual).isEqualTo(TEXT);
+        assertThat(actual).isEqualTo(TEXT_LOWER);
     }
 
     @Test
-    public void event_getViewNodeText_null() {
-        String actual = ContentProtectionUtils.getViewNodeText(EVENT);
+    public void getViewNodeTextLower_null() {
+        String actual = ContentProtectionUtils.getViewNodeTextLower(new ViewNode());
 
         assertThat(actual).isNull();
     }
 
     @Test
-    public void event_getViewNodeText_notNull() {
-        ContentCaptureEvent event = createEvent();
-        event.setViewNode(VIEW_NODE_WITH_TEXT);
+    public void getViewNodeTextLower_notNull() {
+        String actual = ContentProtectionUtils.getViewNodeTextLower(createViewNodeWithText());
 
-        String actual = ContentProtectionUtils.getViewNodeText(event);
-
-        assertThat(actual).isEqualTo(TEXT);
+        assertThat(actual).isEqualTo(TEXT_LOWER);
     }
 
     @Test
-    public void viewNode_getViewNodeText_null() {
-        String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE);
+    public void getHintTextLower_null() {
+        String actual = ContentProtectionUtils.getHintTextLower(new ViewNode());
 
         assertThat(actual).isNull();
     }
 
     @Test
-    public void viewNode_getViewNodeText_notNull() {
-        String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE_WITH_TEXT);
+    public void getHintTextLower_notNull() {
+        String actual = ContentProtectionUtils.getHintTextLower(createViewNodeWithHint());
 
-        assertThat(actual).isEqualTo(TEXT);
+        assertThat(actual).isEqualTo(TEXT_LOWER);
     }
 
     private static ContentCaptureEvent createEvent() {
         return new ContentCaptureEvent(/* sessionId= */ 123, TYPE_SESSION_STARTED);
     }
 
-    private static ViewNode createViewNodeWithText() {
+    private static ContentCaptureEvent createEventWithText() {
+        ContentCaptureEvent event = createEvent();
+        event.setText(TEXT);
+        return event;
+    }
+
+    private static ViewStructureImpl createViewStructureImpl() {
         View view = new View(ApplicationProvider.getApplicationContext());
-        ViewStructureImpl viewStructure = new ViewStructureImpl(view);
-        viewStructure.setText(TEXT);
-        return viewStructure.getNode();
+        return new ViewStructureImpl(view);
+    }
+
+    private static ViewNode createViewNodeWithText() {
+        ViewStructureImpl viewStructureImpl = createViewStructureImpl();
+        viewStructureImpl.setText(TEXT);
+        return viewStructureImpl.getNode();
+    }
+
+    private static ViewNode createViewNodeWithHint() {
+        ViewStructureImpl viewStructureImpl = createViewStructureImpl();
+        viewStructureImpl.setHint(TEXT);
+        return viewStructureImpl.getNode();
     }
 }
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 31c2eb2..b7ea04f 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -128,25 +128,6 @@
     }
 
     /**
-     * Queries user state from Keystore 2.0.
-     *
-     * @param userId - Android user id of the user.
-     * @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) {
-            Log.e(TAG, "getState failed", e);
-            return e.errorCode;
-        } catch (Exception e) {
-            Log.e(TAG, "Can not connect to keystore", e);
-            return SYSTEM_ERROR;
-        }
-    }
-
-    /**
      * Informs Keystore 2.0 that an off body event was detected.
      */
     public static void onDeviceOffBody() {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8045f55..11b8271 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -19,8 +19,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.StrictMode;
-import android.os.UserHandle;
-import android.security.maintenance.UserState;
 
 /**
  * @hide This should not be made public in its present form because it
@@ -37,15 +35,6 @@
     // Used for UID field to indicate the calling UID.
     public static final int UID_SELF = -1;
 
-    // States
-    public enum State {
-        @UnsupportedAppUsage
-        UNLOCKED,
-        @UnsupportedAppUsage
-        LOCKED,
-        UNINITIALIZED
-    };
-
     private static final KeyStore KEY_STORE = new KeyStore();
 
     @UnsupportedAppUsage
@@ -55,28 +44,6 @@
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public State state(int userId) {
-        int userState = AndroidKeyStoreMaintenance.getState(userId);
-        switch (userState) {
-            case UserState.UNINITIALIZED:
-                return KeyStore.State.UNINITIALIZED;
-            case UserState.LSKF_UNLOCKED:
-                return KeyStore.State.UNLOCKED;
-            case UserState.LSKF_LOCKED:
-                return KeyStore.State.LOCKED;
-            default:
-                throw new AssertionError(userState);
-        }
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public State state() {
-        return state(UserHandle.myUserId());
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public byte[] get(String key) {
         return null;
     }
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 96c257b..1ba41b1 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -75,16 +75,18 @@
  * {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey}
  * interfaces.
  *
- * <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in
- * the Android Keystore. This is because the {@link java.security.KeyStore} abstraction does not
- * support storing key pairs without a certificate. The subject, serial number, and validity dates
- * of the certificate can be customized in this spec. The self-signed certificate may be replaced at
- * a later time by a certificate signed by a Certificate Authority (CA).
+ * <p>For asymmetric key pairs, a X.509 certificate will be also generated and stored in the Android
+ * Keystore. This is because the {@link java.security.KeyStore} abstraction does not support storing
+ * key pairs without a certificate. The subject, serial number, and validity dates of the
+ * certificate can be customized in this spec. The certificate may be replaced at a later time by a
+ * certificate signed by a Certificate Authority (CA).
  *
- * <p>NOTE: If a private key is not authorized to sign the self-signed certificate, then the
- * certificate will be created with an invalid signature which will not verify. Such a certificate
- * is still useful because it provides access to the public key. To generate a valid signature for
- * the certificate the key needs to be authorized for all of the following:
+ * <p>NOTE: If attestation is not requested using {@link Builder#setAttestationChallenge(byte[])},
+ * generated certificate may be self-signed. If a private key is not authorized to sign the
+ * certificate, then the certificate will be created with an invalid signature which will not
+ * verify. Such a certificate is still useful because it provides access to the public key. To
+ * generate a valid signature for the certificate the key needs to be authorized for all of the
+ * following:
  * <ul>
  * <li>{@link KeyProperties#PURPOSE_SIGN},</li>
  * <li>operation without requiring the user to be authenticated (see
@@ -989,12 +991,6 @@
          * @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be
          *        used. Attempts to use the key for any other purpose will be rejected.
          *
-         *        <p>If the set of purposes for which the key can be used does not contain
-         *        {@link KeyProperties#PURPOSE_SIGN}, the self-signed certificate generated by
-         *        {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an
-         *        invalid signature. This is OK if the certificate is only used for obtaining the
-         *        public key from Android KeyStore.
-         *
          *        <p>See {@link KeyProperties}.{@code PURPOSE} flags.
          */
         public Builder(@NonNull String keystoreAlias, @KeyProperties.PurposeEnum int purposes) {
@@ -1140,7 +1136,7 @@
         }
 
         /**
-         * Sets the subject used for the self-signed certificate of the generated key pair.
+         * Sets the subject used for the certificate of the generated key pair.
          *
          * <p>By default, the subject is {@code CN=fake}.
          */
@@ -1154,7 +1150,7 @@
         }
 
         /**
-         * Sets the serial number used for the self-signed certificate of the generated key pair.
+         * Sets the serial number used for the certificate of the generated key pair.
          *
          * <p>By default, the serial number is {@code 1}.
          */
@@ -1168,8 +1164,7 @@
         }
 
         /**
-         * Sets the start of the validity period for the self-signed certificate of the generated
-         * key pair.
+         * Sets the start of the validity period for the certificate of the generated key pair.
          *
          * <p>By default, this date is {@code Jan 1 1970}.
          */
@@ -1183,8 +1178,7 @@
         }
 
         /**
-         * Sets the end of the validity period for the self-signed certificate of the generated key
-         * pair.
+         * Sets the end of the validity period for the certificate of the generated key pair.
          *
          * <p>By default, this date is {@code Jan 1 2048}.
          */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 06ce371..8cf869b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -87,33 +87,28 @@
         mTransitions.addHandler(this);
     }
 
-    @Override
-    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        boolean containsEmbeddingSplit = false;
-        boolean containsNonEmbeddedChange = false;
-        final List<TransitionInfo.Change> changes = info.getChanges();
-        for (int i = changes.size() - 1; i >= 0; i--) {
-            final TransitionInfo.Change change = changes.get(i);
-            if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
-                containsNonEmbeddedChange = true;
-            } else if (!change.hasFlags(FLAG_FILLS_TASK)) {
+    /** Whether ActivityEmbeddingController should animate this transition. */
+    public boolean shouldAnimate(@NonNull TransitionInfo info) {
+        boolean containsEmbeddingChange = false;
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if (!change.hasFlags(FLAG_FILLS_TASK) && change.hasFlags(
+                    FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
                 // Whether the Task contains any ActivityEmbedding split before or after the
                 // transition.
-                containsEmbeddingSplit = true;
+                containsEmbeddingChange = true;
             }
         }
-        if (!containsEmbeddingSplit) {
+        if (!containsEmbeddingChange) {
             // Let the system to play the default animation if there is no ActivityEmbedding split
             // window. This allows to play the app customized animation when there is no embedding,
             // such as the device is in a folded state.
             return false;
         }
-        if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) {
+
+        if (containsNonEmbeddedChange(info) && !handleNonEmbeddedChanges(info.getChanges())) {
             return false;
         }
+
         final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
         if (options != null
                 // Scene-transition will be handled by app side.
@@ -123,6 +118,17 @@
             return false;
         }
 
+        return true;
+    }
+
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+        if (!shouldAnimate(info)) return false;
+
         // Start ActivityEmbedding animation.
         mTransitionCallbacks.put(transition, finishCallback);
         mAnimationRunner.startAnimation(transition, info, startTransaction, finishTransaction);
@@ -136,6 +142,16 @@
         mAnimationRunner.cancelAnimationFromMerge();
     }
 
+    /** Whether TransitionInfo contains non-ActivityEmbedding embedded window. */
+    private boolean containsNonEmbeddedChange(@NonNull TransitionInfo info) {
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
         final Rect nonClosingEmbeddedArea = new Rect();
         for (int i = changes.size() - 1; i >= 0; i--) {
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 14a040a..a533ca5 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
@@ -32,6 +32,7 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.BubbleData;
 import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -366,11 +367,12 @@
             KeyguardTransitionHandler keyguardTransitionHandler,
             Optional<DesktopTasksController> desktopTasksController,
             Optional<UnfoldTransitionHandler> unfoldHandler,
+            Optional<ActivityEmbeddingController> activityEmbeddingController,
             Transitions transitions) {
         return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
                 pipTransitionController, recentsTransitionHandler,
                 keyguardTransitionHandler, desktopTasksController,
-                unfoldHandler);
+                unfoldHandler, activityEmbeddingController);
     }
 
     @WMSingleton
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 d31476c..d277eef 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
@@ -925,19 +925,8 @@
                 if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
                 else wct.restoreTransientOrder(mRecentsTask);
             }
-            if (!toHome
-                    // If a recents gesture starts on the 3p launcher, then the 3p launcher is the
-                    // live tile (pausing app). If the gesture is "cancelled" we need to return to
-                    // 3p launcher instead of "task-switching" away from it.
-                    && (!mWillFinishToHome || mPausingSeparateHome)
-                    && mPausingTasks != null && mState == STATE_NORMAL) {
-                if (mPausingSeparateHome) {
-                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
-                            "  returning to 3p home");
-                } else {
-                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
-                            "  returning to app");
-                }
+            if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "  returning to app");
                 // The gesture is returning to the pausing-task(s) rather than continuing with
                 // recents, so end the transition by moving the app back to the top (and also
                 // re-showing it's task).
@@ -969,6 +958,15 @@
                     wct.restoreTransientOrder(mRecentsTask);
                 }
             } else {
+                if (mPausingSeparateHome) {
+                    if (mOpeningTasks.isEmpty()) {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "  recents occluded 3p home");
+                    } else {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "  switch task by recents on 3p home");
+                    }
+                }
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "  normal finish");
                 // The general case: committing to recents, going home, or switching tasks.
                 for (int i = 0; i < mOpeningTasks.size(); ++i) {
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 68ca231..7a4834c 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
@@ -23,6 +23,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -395,6 +396,13 @@
         return mMainStage.isActive();
     }
 
+    /** @return whether this transition-request has the launch-adjacent flag. */
+    public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) {
+        final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+        return triggerTask != null && triggerTask.baseIntent != null
+                && (triggerTask.baseIntent.getFlags() & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0;
+    }
+
     /** @return whether the transition-request implies entering pip from split. */
     public boolean requestImpliesSplitToPip(TransitionRequestInfo request) {
         if (!isSplitActive() || !mMixedHandler.requestHasPipEnter(request)) {
@@ -2459,10 +2467,20 @@
                         EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
             }
 
-            // When split in the background, it should be only opening/dismissing transition and
-            // would keep out not empty. Prevent intercepting all transitions for split screen when
-            // it is in the background and not identify to handle it.
-            return (!out.isEmpty() || isSplitScreenVisible()) ? out : null;
+            if (!out.isEmpty()) {
+                // One of the cases above handled it
+                return out;
+            } else if (isSplitScreenVisible()) {
+                // If split is visible, only defer handling this transition if it's launching
+                // adjacent while there is already a split pair -- this may trigger PIP and
+                // that should be handled by the mixed handler.
+                final boolean deferTransition = requestHasLaunchAdjacentFlag(request)
+                    && mMainStage.getChildCount() != 0 && mSideStage.getChildCount() != 0;
+                return !deferTransition ? out : null;
+            }
+            // Don't intercept the transition if we are not handling it as a part of one of the
+            // cases above and it is not already visible
+            return null;
         } else {
             if (isOpening && getStageOfTask(triggerTask) != null) {
                 // One task is appearing into split, prepare to enter split screen.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 451e618..918a5a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -21,7 +21,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -43,6 +45,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -74,6 +77,7 @@
     private final KeyguardTransitionHandler mKeyguardHandler;
     private DesktopTasksController mDesktopTasksController;
     private UnfoldTransitionHandler mUnfoldHandler;
+    private ActivityEmbeddingController mActivityEmbeddingController;
 
     private static class MixedTransition {
         static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -93,9 +97,12 @@
         /** Recents Transition while in desktop mode. */
         static final int TYPE_RECENTS_DURING_DESKTOP = 6;
 
-        /** Fuld/Unfold transition. */
+        /** Fold/Unfold transition. */
         static final int TYPE_UNFOLD = 7;
 
+        /** Enter pip from one of the Activity Embedding windows. */
+        static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8;
+
         /** The default animation for this mixed transition. */
         static final int ANIM_TYPE_DEFAULT = 0;
 
@@ -150,7 +157,8 @@
             Optional<RecentsTransitionHandler> recentsHandlerOptional,
             KeyguardTransitionHandler keyguardHandler,
             Optional<DesktopTasksController> desktopTasksControllerOptional,
-            Optional<UnfoldTransitionHandler> unfoldHandler) {
+            Optional<UnfoldTransitionHandler> unfoldHandler,
+            Optional<ActivityEmbeddingController> activityEmbeddingController) {
         mPlayer = player;
         mKeyguardHandler = keyguardHandler;
         if (Transitions.ENABLE_SHELL_TRANSITIONS
@@ -170,6 +178,7 @@
                 }
                 mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
                 mUnfoldHandler = unfoldHandler.orElse(null);
+                mActivityEmbeddingController = activityEmbeddingController.orElse(null);
             }, this);
         }
     }
@@ -192,6 +201,16 @@
             mPipHandler.augmentRequest(transition, request, out);
             mSplitHandler.addEnterOrExitIfNeeded(request, out);
             return out;
+        } else if (request.getType() == TRANSIT_PIP
+                && (request.getFlags() & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0 && (
+                mActivityEmbeddingController != null)) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                    " Got a PiP-enter request from an Activity Embedding split");
+            mActiveTransitions.add(new MixedTransition(
+                    MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
+            // Postpone transition splitting to later.
+            WindowContainerTransaction out = new WindowContainerTransaction();
+            return out;
         } else if (request.getRemoteTransition() != null
                 && TransitionUtil.isOpeningType(request.getType())
                 && (request.getTriggerTask() == null
@@ -355,6 +374,9 @@
         if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
             return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
                     finishCallback);
+        } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+            return animateEnterPipFromActivityEmbedding(mixed, info, startTransaction,
+                    finishTransaction, finishCallback);
         } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
             return false;
         } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
@@ -400,6 +422,58 @@
         }
     }
 
+    private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed,
+            @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+                + "entering PIP from an Activity Embedding window");
+        // Split into two transitions (wct)
+        TransitionInfo.Change pipChange = null;
+        final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            TransitionInfo.Change change = info.getChanges().get(i);
+            if (mPipHandler.isEnteringPip(change, info.getType())) {
+                if (pipChange != null) {
+                    throw new IllegalStateException("More than 1 pip-entering changes in one"
+                            + " transition? " + info);
+                }
+                pipChange = change;
+                // going backwards, so remove-by-index is fine.
+                everythingElse.getChanges().remove(i);
+            }
+        }
+
+        final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+            --mixed.mInFlightSubAnimations;
+            mixed.joinFinishArgs(wct);
+            if (mixed.mInFlightSubAnimations > 0) return;
+            mActiveTransitions.remove(mixed);
+            finishCallback.onTransitionFinished(mixed.mFinishWCT);
+        };
+
+        if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
+            // Fallback to dispatching to other handlers.
+            return false;
+        }
+
+        // PIP window should always be on the highest Z order.
+        if (pipChange != null) {
+            mixed.mInFlightSubAnimations = 2;
+            mPipHandler.startEnterAnimation(
+                    pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+                    finishTransaction,
+                    finishCB);
+        } else {
+            mixed.mInFlightSubAnimations = 1;
+        }
+
+        mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse,
+                startTransaction, finishTransaction, finishCB);
+        return true;
+    }
+
     private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed,
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
@@ -811,6 +885,10 @@
                 } else {
                     mPipHandler.end();
                 }
+            } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+                mPipHandler.end();
+                mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
+                        finishCallback);
             } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
                 mPipHandler.end();
                 if (mixed.mLeftoversHandler != null) {
@@ -851,6 +929,9 @@
         if (mixed == null) return;
         if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
             mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+        } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+            mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
         } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
             mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
         } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
diff --git a/media/java/android/media/RingtoneV1.java b/media/java/android/media/RingtoneV1.java
index b761afa..3c54d4a 100644
--- a/media/java/android/media/RingtoneV1.java
+++ b/media/java/android/media/RingtoneV1.java
@@ -16,14 +16,15 @@
 
 package android.media;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources.NotFoundException;
 import android.media.audiofx.HapticGenerator;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.VibrationEffect;
@@ -61,7 +62,6 @@
 
     private final Context mContext;
     private final AudioManager mAudioManager;
-    private final Ringtone.Injectables mInjectables;
     private VolumeShaper.Configuration mVolumeShaperConfig;
     private VolumeShaper mVolumeShaper;
 
@@ -74,10 +74,12 @@
     private final IRingtonePlayer mRemotePlayer;
     private final Binder mRemoteToken;
 
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private MediaPlayer mLocalPlayer;
     private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
     private HapticGenerator mHapticGenerator;
 
+    @UnsupportedAppUsage
     private Uri mUri;
     private String mTitle;
 
@@ -92,15 +94,10 @@
     private boolean mHapticGeneratorEnabled = false;
     private final Object mPlaybackSettingsLock = new Object();
 
-    /** @hide */
+    /** {@hide} */
+    @UnsupportedAppUsage
     public RingtoneV1(Context context, boolean allowRemote) {
-        this(context, new Ringtone.Injectables(), allowRemote);
-    }
-
-    /** @hide */
-    RingtoneV1(Context context, @NonNull Ringtone.Injectables injectables, boolean allowRemote) {
         mContext = context;
-        mInjectables = injectables;
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         mAllowRemote = allowRemote;
         mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
@@ -203,7 +200,7 @@
         }
         destroyLocalPlayer();
         // try opening uri locally before delegating to remote player
-        mLocalPlayer = mInjectables.newMediaPlayer();
+        mLocalPlayer = new MediaPlayer();
         try {
             mLocalPlayer.setDataSource(mContext, mUri);
             mLocalPlayer.setAudioAttributes(mAudioAttributes);
@@ -243,7 +240,19 @@
      */
     public boolean hasHapticChannels() {
         // FIXME: support remote player, or internalize haptic channels support and remove entirely.
-        return mInjectables.hasHapticChannels(mLocalPlayer);
+        try {
+            android.os.Trace.beginSection("Ringtone.hasHapticChannels");
+            if (mLocalPlayer != null) {
+                for(MediaPlayer.TrackInfo trackInfo : mLocalPlayer.getTrackInfo()) {
+                    if (trackInfo.hasHapticChannels()) {
+                        return true;
+                    }
+                }
+            }
+        } finally {
+            android.os.Trace.endSection();
+        }
+        return false;
     }
 
     /**
@@ -325,7 +334,7 @@
      * @see android.media.audiofx.HapticGenerator#isAvailable()
      */
     public boolean setHapticGeneratorEnabled(boolean enabled) {
-        if (!mInjectables.isHapticGeneratorAvailable()) {
+        if (!HapticGenerator.isAvailable()) {
             return false;
         }
         synchronized (mPlaybackSettingsLock) {
@@ -353,7 +362,7 @@
             mLocalPlayer.setVolume(mVolume);
             mLocalPlayer.setLooping(mIsLooping);
             if (mHapticGenerator == null && mHapticGeneratorEnabled) {
-                mHapticGenerator = mInjectables.createHapticGenerator(mLocalPlayer);
+                mHapticGenerator = HapticGenerator.create(mLocalPlayer.getAudioSessionId());
             }
             if (mHapticGenerator != null) {
                 mHapticGenerator.setEnabled(mHapticGeneratorEnabled);
@@ -388,6 +397,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUri(Uri uri) {
         setUri(uri, null);
     }
@@ -415,6 +425,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public Uri getUri() {
         return mUri;
     }
@@ -545,7 +556,7 @@
                 Log.e(TAG, "Could not load fallback ringtone");
                 return false;
             }
-            mLocalPlayer = mInjectables.newMediaPlayer();
+            mLocalPlayer = new MediaPlayer();
             if (afd.getDeclaredLength() < 0) {
                 mLocalPlayer.setDataSource(afd.getFileDescriptor());
             } else {
@@ -583,12 +594,12 @@
     }
 
     public boolean isLocalOnly() {
-        return !mAllowRemote;
+        return mAllowRemote;
     }
 
     public boolean isUsingRemotePlayer() {
         // V2 testing api, but this is the v1 approximation.
-        return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null) && (mUri != null);
+        return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null);
     }
 
     class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
diff --git a/media/java/android/media/midi/MidiUmpDeviceService.java b/media/java/android/media/midi/MidiUmpDeviceService.java
index bbbe7f6..c54bfce 100644
--- a/media/java/android/media/midi/MidiUmpDeviceService.java
+++ b/media/java/android/media/midi/MidiUmpDeviceService.java
@@ -16,6 +16,9 @@
 
 package android.media.midi;
 
+import static com.android.media.midi.flags.Flags.FLAG_VIRTUAL_UMP;
+
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Service;
@@ -54,9 +57,11 @@
  *             android:resource="@xml/device_info" />
  * &lt;/service></pre>
  */
+@FlaggedApi(FLAG_VIRTUAL_UMP)
 public abstract class MidiUmpDeviceService extends Service {
     private static final String TAG = "MidiUmpDeviceService";
 
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
 
     private IMidiManager mMidiManager;
@@ -75,6 +80,7 @@
         }
     };
 
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     @Override
     public void onCreate() {
         mMidiManager = IMidiManager.Stub.asInterface(
@@ -112,6 +118,7 @@
      * The number of input and output ports must be equal and non-zero.
      * @return list of MidiReceivers
      */
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     public abstract @NonNull List<MidiReceiver> onGetInputPortReceivers();
 
     /**
@@ -120,6 +127,7 @@
      * The number of input and output ports must be equal and non-zero.
      * @return the list of MidiReceivers
      */
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     public final @NonNull List<MidiReceiver> getOutputPortReceivers() {
         if (mServer == null) {
             return new ArrayList<MidiReceiver>();
@@ -132,6 +140,7 @@
      * Returns the {@link MidiDeviceInfo} instance for this service
      * @return the MidiDeviceInfo of the virtual MIDI device if it was successfully created
      */
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     public final @Nullable MidiDeviceInfo getDeviceInfo() {
         return mDeviceInfo;
     }
@@ -140,6 +149,7 @@
      * Called to notify when the {@link MidiDeviceStatus} has changed
      * @param status the current status of the MIDI device
      */
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     public void onDeviceStatusChanged(@NonNull MidiDeviceStatus status) {
     }
 
@@ -147,9 +157,11 @@
      * Called to notify when the virtual MIDI device running in this service has been closed by
      * all its clients
      */
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     public void onClose() {
     }
 
+    @FlaggedApi(FLAG_VIRTUAL_UMP)
     @Override
     public @Nullable IBinder onBind(@NonNull Intent intent) {
         if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 80e2247..31e65eb 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -175,5 +175,5 @@
     @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
+    oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
 }
diff --git a/media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
similarity index 69%
rename from media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java
rename to media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
index 2c8daba..3c0c684 100644
--- a/media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
@@ -14,22 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.media;
+package com.android.mediaframeworktest.unit;
 
 import static android.media.Ringtone.MEDIA_SOUND;
 import static android.media.Ringtone.MEDIA_SOUND_AND_VIBRATION;
 import static android.media.Ringtone.MEDIA_VIBRATION;
 
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerFallbackSetup;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerSetup;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerStarted;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerStopped;
-
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
@@ -55,29 +53,34 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.testing.TestableContext;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.framework.base.media.ringtone.tests.R;
-import com.android.media.testing.RingtoneInjectablesTrackingTestRule;
+import com.android.mediaframeworktest.R;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
 import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayDeque;
+import java.util.Map;
+import java.util.Queue;
 
-/**
- * Test behavior of {@link Ringtone} when it's created via {@link Ringtone.Builder}.
- */
 @RunWith(AndroidJUnit4.class)
-public class RingtoneBuilderTest {
+public class RingtoneTest {
 
     private static final Uri SOUND_URI = Uri.parse("content://fake-sound-uri");
 
@@ -90,8 +93,11 @@
 
     private static final VibrationEffect VIBRATION_EFFECT =
             VibrationEffect.createWaveform(new long[] { 0, 100, 50, 100}, -1);
+    private static final VibrationEffect VIBRATION_EFFECT_REPEATING =
+            VibrationEffect.createWaveform(new long[] { 0, 100, 50, 100, 50}, 1);
 
-    @Rule public final RingtoneInjectablesTrackingTestRule
+    @Rule
+    public final RingtoneInjectablesTrackingTestRule
             mMediaPlayerRule = new RingtoneInjectablesTrackingTestRule();
 
     @Captor private ArgumentCaptor<IBinder> mIBinderCaptor;
@@ -116,7 +122,6 @@
         mContext = spy(testContext);
     }
 
-
     @Test
     public void testRingtone_fullLifecycleUsingLocalMediaPlayer() throws Exception {
         MediaPlayer mockMediaPlayer = mMediaPlayerRule.expectLocalMediaPlayer();
@@ -137,14 +142,14 @@
         assertThat(ringtone.isLocalOnly()).isFalse();
 
         // Prepare
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES);
         verify(mockMediaPlayer).setVolume(1.0f);
         verify(mockMediaPlayer).setLooping(false);
         verify(mockMediaPlayer).prepare();
 
         // Play
         ringtone.play();
-        verifyPlayerStarted(mockMediaPlayer);
+        verifyLocalPlay(mockMediaPlayer);
 
         // Verify dynamic controls.
         ringtone.setVolume(0.8f);
@@ -160,7 +165,7 @@
 
         // Release
         ringtone.stop();
-        verifyPlayerStopped(mockMediaPlayer);
+        verifyLocalStop(mockMediaPlayer);
 
         // This test is intended to strictly verify all interactions with MediaPlayer in a local
         // playback case. This shouldn't be necessary in other tests that have the same basic
@@ -194,16 +199,16 @@
         assertThat(ringtone.getAudioAttributes()).isEqualTo(audioAttributes);
 
         // Prepare
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, audioAttributes);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, audioAttributes);
         verify(mockMediaPlayer).prepare();
 
         // Play
         ringtone.play();
-        verifyPlayerStarted(mockMediaPlayer);
+        verifyLocalPlay(mockMediaPlayer);
 
         // Release
         ringtone.stop();
-        verifyPlayerStopped(mockMediaPlayer);
+        verifyLocalStop(mockMediaPlayer);
 
         verifyZeroInteractions(mMockRemotePlayer);
         verifyZeroInteractions(mMockVibrator);
@@ -215,8 +220,8 @@
         setupFileNotFound(mockMediaPlayer, SOUND_URI);
         Ringtone ringtone =
                 newBuilder(MEDIA_SOUND, RINGTONE_ATTRIBUTES)
-                        .setUri(SOUND_URI)
-                        .build();
+                .setUri(SOUND_URI)
+                .build();
         assertThat(ringtone).isNotNull();
         assertThat(ringtone.isUsingRemotePlayer()).isTrue();
 
@@ -279,7 +284,7 @@
         // Prepare
         // Uses attributes with haptic channels enabled, but will use the effect when there aren't
         // any present.
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
         verify(mockMediaPlayer).setVolume(1.0f);
         verify(mockMediaPlayer).setLooping(false);
         verify(mockMediaPlayer).prepare();
@@ -287,7 +292,7 @@
         // Play
         ringtone.play();
 
-        verifyPlayerStarted(mockMediaPlayer);
+        verifyLocalPlay(mockMediaPlayer);
         verify(mMockVibrator).vibrate(VIBRATION_EFFECT, RINGTONE_VIB_ATTRIBUTES);
 
         // Verify dynamic controls.
@@ -305,7 +310,7 @@
 
         // Release
         ringtone.stop();
-        verifyPlayerStopped(mockMediaPlayer);
+        verifyLocalStop(mockMediaPlayer);
         verify(mMockVibrator).cancel(VibrationAttributes.USAGE_RINGTONE);
 
         // This test is intended to strictly verify all interactions with MediaPlayer in a local
@@ -383,7 +388,7 @@
         // Prepare
         // Uses attributes with haptic channels enabled, but will abandon the MediaPlayer when it
         // knows there aren't any.
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
         verify(mockMediaPlayer).setVolume(0.0f);  // Vibration-only: sound muted.
         verify(mockMediaPlayer).setLooping(false);
         verify(mockMediaPlayer).prepare();
@@ -438,7 +443,7 @@
         // Prepare
         // Uses attributes with haptic channels enabled, but will use the effect when there aren't
         // any present.
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
         verify(mockMediaPlayer).setVolume(0.0f);  // Vibration-only: sound muted.
         verify(mockMediaPlayer).setLooping(false);
         verify(mockMediaPlayer).prepare();
@@ -446,7 +451,7 @@
         // Play
         ringtone.play();
         // Vibrator.vibrate isn't called because the vibration comes from the sound.
-        verifyPlayerStarted(mockMediaPlayer);
+        verifyLocalPlay(mockMediaPlayer);
 
         // Verify dynamic controls (no-op without sound)
         ringtone.setVolume(0.8f);
@@ -461,7 +466,7 @@
 
         // Release
         ringtone.stop();
-        verifyPlayerStopped(mockMediaPlayer);
+        verifyLocalStop(mockMediaPlayer);
 
         // This test is intended to strictly verify all interactions with MediaPlayer in a local
         // playback case. This shouldn't be necessary in other tests that have the same basic
@@ -491,17 +496,17 @@
 
         // Prepare
         // The attributes here have haptic channels enabled (unlike above)
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
         verify(mockMediaPlayer).prepare();
 
         // Play
         ringtone.play();
         when(mockMediaPlayer.isPlaying()).thenReturn(true);
-        verifyPlayerStarted(mockMediaPlayer);
+        verifyLocalPlay(mockMediaPlayer);
 
         // Release
         ringtone.stop();
-        verifyPlayerStopped(mockMediaPlayer);
+        verifyLocalStop(mockMediaPlayer);
 
         verifyZeroInteractions(mMockRemotePlayer);
         // Nothing after the initial hasVibrator - it uses audio-coupled.
@@ -531,7 +536,7 @@
 
         // Prepare
         // The attributes here have haptic channels enabled (unlike above)
-        verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+        verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
         verify(mockMediaPlayer).prepare();
 
         // Play
@@ -554,7 +559,7 @@
     @Test
     public void testRingtone_nullMediaOnBuilderUsesFallback() throws Exception {
         AssetFileDescriptor testResourceFd =
-                mContext.getResources().openRawResourceFd(R.raw.test_sound_file);
+                mContext.getResources().openRawResourceFd(R.raw.shortmp3);
         // Ensure it will flow as expected.
         assertThat(testResourceFd).isNotNull();
         assertThat(testResourceFd.getDeclaredLength()).isAtLeast(0);
@@ -570,18 +575,18 @@
 
         // Delegates straight to fallback in local player.
         // Prepare
-        verifyPlayerFallbackSetup(mockMediaPlayer, testResourceFd, RINGTONE_ATTRIBUTES);
+        verifyLocalPlayerFallbackSetup(mockMediaPlayer, testResourceFd, RINGTONE_ATTRIBUTES);
         verify(mockMediaPlayer).setVolume(1.0f);
         verify(mockMediaPlayer).setLooping(false);
         verify(mockMediaPlayer).prepare();
 
         // Play
         ringtone.play();
-        verifyPlayerStarted(mockMediaPlayer);
+        verifyLocalPlay(mockMediaPlayer);
 
         // Release
         ringtone.stop();
-        verifyPlayerStopped(mockMediaPlayer);
+        verifyLocalStop(mockMediaPlayer);
 
         verifyNoMoreInteractions(mockMediaPlayer);
         verifyNoMoreInteractions(mMockRemotePlayer);
@@ -610,10 +615,24 @@
         verifyNoMoreInteractions(mMockRemotePlayer);
     }
 
+    @Test
+    public void testRingtone_noMediaSetOnBuilderFallbackFailsAndNoRemote() throws Exception {
+        mContext.getOrCreateTestableResources()
+                .addOverride(com.android.internal.R.raw.fallbackring, null);
+        Ringtone ringtone = newBuilder(MEDIA_SOUND, RINGTONE_ATTRIBUTES)
+                .setUri(null)
+                .setLocalOnly()
+                .build();
+        // Local player fallback fails as the resource isn't found (no media player creation is
+        // attempted), and since there is no local player, the ringtone ends up having nothing to
+        // do.
+        assertThat(ringtone).isNull();
+    }
+
     private Ringtone.Builder newBuilder(@Ringtone.RingtoneMedia int ringtoneMedia,
             AudioAttributes audioAttributes) {
         return new Ringtone.Builder(mContext, ringtoneMedia, audioAttributes)
-                .setInjectables(mMediaPlayerRule.getRingtoneTestInjectables());
+                .setInjectables(mMediaPlayerRule.injectables);
     }
 
     private static AudioAttributes audioAttributes(int audioUsage) {
@@ -628,4 +647,194 @@
         doThrow(new FileNotFoundException("Fake file not found"))
                 .when(mockMediaPlayer).setDataSource(any(Context.class), eq(uri));
     }
+
+    private void verifyLocalPlayerSetup(MediaPlayer mockPlayer, Uri expectedUri,
+            AudioAttributes expectedAudioAttributes) throws Exception {
+        verify(mockPlayer).setDataSource(mContext, expectedUri);
+        verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
+        verify(mockPlayer).setPreferredDevice(null);
+        verify(mockPlayer).prepare();
+    }
+
+    private void verifyLocalPlayerFallbackSetup(MediaPlayer mockPlayer, AssetFileDescriptor afd,
+            AudioAttributes expectedAudioAttributes) throws Exception {
+        // This is very specific but it's a simple way to test that the test resource matches.
+        if (afd.getDeclaredLength() < 0) {
+            verify(mockPlayer).setDataSource(afd.getFileDescriptor());
+        } else {
+            verify(mockPlayer).setDataSource(afd.getFileDescriptor(),
+                    afd.getStartOffset(),
+                    afd.getDeclaredLength());
+        }
+        verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
+        verify(mockPlayer).setPreferredDevice(null);
+        verify(mockPlayer).prepare();
+    }
+
+    private void verifyLocalPlay(MediaPlayer mockMediaPlayer) {
+        verify(mockMediaPlayer).setOnCompletionListener(any());
+        verify(mockMediaPlayer).start();
+    }
+
+    private void verifyLocalStop(MediaPlayer mockMediaPlayer) {
+        verify(mockMediaPlayer).stop();
+        verify(mockMediaPlayer).setOnCompletionListener(isNull());
+        verify(mockMediaPlayer).reset();
+        verify(mockMediaPlayer).release();
+    }
+
+    /**
+     * This rule ensures that all expected media player creations from the factory do actually
+     * occur. The reason for this level of control is that creating a media player is fairly
+     * expensive and blocking, so we do want unit tests of this class to "declare" interactions
+     * of all created media players.
+     *
+     * This needs to be a TestRule so that the teardown assertions can be skipped if the test has
+     * failed (and media player assertions may just be a distracting side effect). Otherwise, the
+     * teardown failures hide the real test ones.
+     */
+    public static class RingtoneInjectablesTrackingTestRule implements TestRule {
+        public Ringtone.Injectables injectables = new TestInjectables();
+        public boolean hapticGeneratorAvailable = true;
+
+        // Queue of (local) media players, in order of expected creation. Enqueue using
+        // expectNewMediaPlayer(), dequeued by the media player factory passed to Ringtone.
+        // This queue is asserted to be empty at the end of the test.
+        private Queue<MediaPlayer> mMockMediaPlayerQueue = new ArrayDeque<>();
+
+        // Similar to media players, but for haptic generator, which also needs releasing.
+        private Map<MediaPlayer, HapticGenerator> mMockHapticGeneratorMap = new ArrayMap<>();
+
+        // Media players with haptic channels.
+        private ArraySet<MediaPlayer> mHapticChannels = new ArraySet<>();
+
+        @Override
+        public Statement apply(Statement base, Description description) {
+            return new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    base.evaluate();
+                    // Only assert if the test didn't fail (base.evaluate() would throw).
+                    assertWithMessage("Test setup an expectLocalMediaPlayer but it wasn't consumed")
+                            .that(mMockMediaPlayerQueue).isEmpty();
+                    // Only assert if the test didn't fail (base.evaluate() would throw).
+                    assertWithMessage(
+                            "Test setup an expectLocalHapticGenerator but it wasn't consumed")
+                            .that(mMockHapticGeneratorMap).isEmpty();
+                }
+            };
+        }
+
+        private TestMediaPlayer expectLocalMediaPlayer() {
+            TestMediaPlayer mockMediaPlayer = Mockito.mock(TestMediaPlayer.class);
+            // Delegate to simulated methods. This means they can be verified but also reflect
+            // realistic transitions from the TestMediaPlayer.
+            doCallRealMethod().when(mockMediaPlayer).start();
+            doCallRealMethod().when(mockMediaPlayer).stop();
+            doCallRealMethod().when(mockMediaPlayer).setLooping(anyBoolean());
+            when(mockMediaPlayer.isLooping()).thenCallRealMethod();
+            when(mockMediaPlayer.isLooping()).thenCallRealMethod();
+            mMockMediaPlayerQueue.add(mockMediaPlayer);
+            return mockMediaPlayer;
+        }
+
+        private HapticGenerator expectHapticGenerator(MediaPlayer mockMediaPlayer) {
+            HapticGenerator mockHapticGenerator = Mockito.mock(HapticGenerator.class);
+            // A test should never want this.
+            assertWithMessage("Can't expect a second haptic generator created "
+                    + "for one media player")
+                    .that(mMockHapticGeneratorMap.put(mockMediaPlayer, mockHapticGenerator))
+                    .isNull();
+            return mockHapticGenerator;
+        }
+
+        private void setHasHapticChannels(MediaPlayer mp, boolean hasHapticChannels) {
+            if (hasHapticChannels) {
+                mHapticChannels.add(mp);
+            } else {
+                mHapticChannels.remove(mp);
+            }
+        }
+
+        private class TestInjectables extends Ringtone.Injectables {
+            @Override
+            public MediaPlayer newMediaPlayer() {
+                assertWithMessage(
+                        "Unexpected MediaPlayer creation. Bug or need expectNewMediaPlayer")
+                        .that(mMockMediaPlayerQueue)
+                        .isNotEmpty();
+                return mMockMediaPlayerQueue.remove();
+            }
+
+            @Override
+            public boolean isHapticGeneratorAvailable() {
+                return hapticGeneratorAvailable;
+            }
+
+            @Override
+            public HapticGenerator createHapticGenerator(MediaPlayer mediaPlayer) {
+                HapticGenerator mockHapticGenerator = mMockHapticGeneratorMap.remove(mediaPlayer);
+                assertWithMessage("Unexpected HapticGenerator creation. "
+                        + "Bug or need expectHapticGenerator")
+                        .that(mockHapticGenerator)
+                        .isNotNull();
+                return mockHapticGenerator;
+            }
+
+            @Override
+            public boolean isHapticPlaybackSupported() {
+                return true;
+            }
+
+            @Override
+            public boolean hasHapticChannels(MediaPlayer mp) {
+                return mHapticChannels.contains(mp);
+            }
+        }
+    }
+
+    /**
+     * MediaPlayer relies on a native backend and so its necessary to intercept calls from
+     * fake usage hitting them.
+     *
+     * Mocks don't work directly on native calls, but if they're overridden then it does work.
+     * Some basic state faking is also done to make the mocks more realistic.
+     */
+    private static class TestMediaPlayer extends MediaPlayer {
+        private boolean mIsPlaying = false;
+        private boolean mIsLooping = false;
+
+        @Override
+        public void start() {
+            mIsPlaying = true;
+        }
+
+        @Override
+        public void stop() {
+            mIsPlaying = false;
+        }
+
+        @Override
+        public void setLooping(boolean value) {
+            mIsLooping = value;
+        }
+
+        @Override
+        public boolean isLooping() {
+            return mIsLooping;
+        }
+
+        @Override
+        public boolean isPlaying() {
+            return mIsPlaying;
+        }
+
+        void simulatePlayingFinished() {
+            if (!mIsPlaying) {
+                throw new IllegalStateException(
+                        "Attempted to pretend playing finished when not playing");
+            }
+            mIsPlaying = false;
+        }
+    }
 }
diff --git a/media/tests/ringtone/Android.bp b/media/tests/ringtone/Android.bp
index 8d1e5e3..55b98c4 100644
--- a/media/tests/ringtone/Android.bp
+++ b/media/tests/ringtone/Android.bp
@@ -9,24 +9,15 @@
     srcs: ["src/**/*.java"],
 
     libs: [
-        "android.test.base",
-        "android.test.mock",
         "android.test.runner",
+        "android.test.base",
     ],
 
     static_libs: [
-        "androidx.test.ext.junit",
-        "androidx.test.ext.truth",
         "androidx.test.rules",
-        "frameworks-base-testutils",
-        "mockito-target-inline-minus-junit4",
-        "testables",
         "testng",
-    ],
-
-    jni_libs: [
-        "libdexmakerjvmtiagent",
-        "libstaticjvmtiagent",
+        "androidx.test.ext.truth",
+        "frameworks-base-testutils",
     ],
 
     test_suites: [
diff --git a/media/tests/ringtone/OWNERS b/media/tests/ringtone/OWNERS
deleted file mode 100644
index 93b44f4..0000000
--- a/media/tests/ringtone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 345036
-
-include /services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java b/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java
deleted file mode 100644
index e97e117..0000000
--- a/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java
+++ /dev/null
@@ -1,75 +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.media.testing;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.AudioAttributes;
-import android.media.MediaPlayer;
-import android.net.Uri;
-
-/**
- * Helper class with assertion methods on mock {@link MediaPlayer} instances.
- */
-public final class MediaPlayerTestHelper {
-
-    /** Verify this local media player mock instance was started. */
-    public static void verifyPlayerStarted(MediaPlayer mockMediaPlayer) {
-        verify(mockMediaPlayer).setOnCompletionListener(any());
-        verify(mockMediaPlayer).start();
-    }
-
-    /** Verify this local media player mock instance was stopped and released. */
-    public static void verifyPlayerStopped(MediaPlayer mockMediaPlayer) {
-        verify(mockMediaPlayer).stop();
-        verify(mockMediaPlayer).setOnCompletionListener(isNull());
-        verify(mockMediaPlayer).reset();
-        verify(mockMediaPlayer).release();
-    }
-
-    /** Verify this local media player mock instance was setup with given attributes. */
-    public static void verifyPlayerSetup(Context context, MediaPlayer mockPlayer,
-            Uri expectedUri, AudioAttributes expectedAudioAttributes) throws Exception {
-        verify(mockPlayer).setDataSource(context, expectedUri);
-        verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
-        verify(mockPlayer).setPreferredDevice(null);
-        verify(mockPlayer).prepare();
-    }
-
-    /** Verify this local media player mock instance was setup with given fallback attributes. */
-    public static void verifyPlayerFallbackSetup(MediaPlayer mockPlayer,
-            AssetFileDescriptor afd, AudioAttributes expectedAudioAttributes) throws Exception {
-        // This is very specific but it's a simple way to test that the test resource matches.
-        if (afd.getDeclaredLength() < 0) {
-            verify(mockPlayer).setDataSource(afd.getFileDescriptor());
-        } else {
-            verify(mockPlayer).setDataSource(afd.getFileDescriptor(),
-                    afd.getStartOffset(),
-                    afd.getDeclaredLength());
-        }
-        verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
-        verify(mockPlayer).setPreferredDevice(null);
-        verify(mockPlayer).prepare();
-    }
-
-    private MediaPlayerTestHelper() {
-    }
-}
diff --git a/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java b/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java
deleted file mode 100644
index 25752ce..0000000
--- a/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java
+++ /dev/null
@@ -1,225 +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.media.testing;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.when;
-
-import android.media.MediaPlayer;
-import android.media.Ringtone;
-import android.media.audiofx.HapticGenerator;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.Mockito;
-
-import java.util.ArrayDeque;
-import java.util.Map;
-import java.util.Queue;
-
-/**
- * This rule ensures that all expected media player creations from the factory do actually
- * occur. The reason for this level of control is that creating a media player is fairly
- * expensive and blocking, so we do want unit tests of this class to "declare" interactions
- * of all created media players.
- * <p>
- * This needs to be a TestRule so that the teardown assertions can be skipped if the test has
- * failed (and media player assertions may just be a distracting side effect). Otherwise, the
- * teardown failures hide the real test ones.
- */
-public class RingtoneInjectablesTrackingTestRule implements TestRule {
-
-    private final Ringtone.Injectables mRingtoneTestInjectables = new TestInjectables();
-
-    // Queue of (local) media players, in order of expected creation. Enqueue using
-    // expectNewMediaPlayer(), dequeued by the media player factory passed to Ringtone.
-    // This queue is asserted to be empty at the end of the test.
-    private final Queue<MediaPlayer> mMockMediaPlayerQueue = new ArrayDeque<>();
-
-    // Similar to media players, but for haptic generator, which also needs releasing.
-    private final Map<MediaPlayer, HapticGenerator> mMockHapticGeneratorMap = new ArrayMap<>();
-
-    // Media players with haptic channels.
-    private final ArraySet<MediaPlayer> mHapticChannels = new ArraySet<>();
-
-    private boolean mHapticGeneratorAvailable = true;
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                base.evaluate();
-                // Only assert if the test didn't fail (base.evaluate() would throw).
-                assertWithMessage("Test setup an expectLocalMediaPlayer but it wasn't consumed")
-                        .that(mMockMediaPlayerQueue).isEmpty();
-                // Only assert if the test didn't fail (base.evaluate() would throw).
-                assertWithMessage(
-                        "Test setup an expectLocalHapticGenerator but it wasn't consumed")
-                        .that(mMockHapticGeneratorMap).isEmpty();
-            }
-        };
-    }
-
-    /** The {@link Ringtone.Injectables} to be used for creating a testable {@link Ringtone}. */
-    public Ringtone.Injectables getRingtoneTestInjectables() {
-        return mRingtoneTestInjectables;
-    }
-
-    /**
-     * Create a test {@link MediaPlayer} that will be provided to the {@link Ringtone} instance
-     * created with {@link #getRingtoneTestInjectables()}.
-     *
-     * <p>If a media player is not created during the test execution after this method is called
-     * then the test will fail. It will also fail if the ringtone attempts to create one without
-     * this method being called first.
-     */
-    public TestMediaPlayer expectLocalMediaPlayer() {
-        TestMediaPlayer mockMediaPlayer = Mockito.mock(TestMediaPlayer.class);
-        // Delegate to simulated methods. This means they can be verified but also reflect
-        // realistic transitions from the TestMediaPlayer.
-        doCallRealMethod().when(mockMediaPlayer).start();
-        doCallRealMethod().when(mockMediaPlayer).stop();
-        doCallRealMethod().when(mockMediaPlayer).setLooping(anyBoolean());
-        when(mockMediaPlayer.isLooping()).thenCallRealMethod();
-        mMockMediaPlayerQueue.add(mockMediaPlayer);
-        return mockMediaPlayer;
-    }
-
-    /**
-     * Create a test {@link HapticGenerator} that will be provided to the {@link Ringtone} instance
-     * created with {@link #getRingtoneTestInjectables()}.
-     *
-     * <p>If a haptic generator is not created during the test execution after this method is called
-     * then the test will fail. It will also fail if the ringtone attempts to create one without
-     * this method being called first.
-     */
-    public HapticGenerator expectHapticGenerator(MediaPlayer mediaPlayer) {
-        HapticGenerator mockHapticGenerator = Mockito.mock(HapticGenerator.class);
-        // A test should never want this.
-        assertWithMessage("Can't expect a second haptic generator created "
-                + "for one media player")
-                .that(mMockHapticGeneratorMap.put(mediaPlayer, mockHapticGenerator))
-                .isNull();
-        return mockHapticGenerator;
-    }
-
-    /**
-     * Configures the {@link MediaPlayer} to always return given flag when
-     * {@link Ringtone.Injectables#hasHapticChannels(MediaPlayer)} is called.
-     */
-    public void setHasHapticChannels(MediaPlayer mp, boolean hasHapticChannels) {
-        if (hasHapticChannels) {
-            mHapticChannels.add(mp);
-        } else {
-            mHapticChannels.remove(mp);
-        }
-    }
-
-    /** Test implementation of {@link Ringtone.Injectables} that uses the test rule setup. */
-    private class TestInjectables extends Ringtone.Injectables {
-        @Override
-        public MediaPlayer newMediaPlayer() {
-            assertWithMessage(
-                    "Unexpected MediaPlayer creation. Bug or need expectNewMediaPlayer")
-                    .that(mMockMediaPlayerQueue)
-                    .isNotEmpty();
-            return mMockMediaPlayerQueue.remove();
-        }
-
-        @Override
-        public boolean isHapticGeneratorAvailable() {
-            return mHapticGeneratorAvailable;
-        }
-
-        @Override
-        public HapticGenerator createHapticGenerator(MediaPlayer mediaPlayer) {
-            HapticGenerator mockHapticGenerator = mMockHapticGeneratorMap.remove(mediaPlayer);
-            assertWithMessage("Unexpected HapticGenerator creation. "
-                    + "Bug or need expectHapticGenerator")
-                    .that(mockHapticGenerator)
-                    .isNotNull();
-            return mockHapticGenerator;
-        }
-
-        @Override
-        public boolean isHapticPlaybackSupported() {
-            return true;
-        }
-
-        @Override
-        public boolean hasHapticChannels(MediaPlayer mp) {
-            return mHapticChannels.contains(mp);
-        }
-    }
-
-    /**
-     * MediaPlayer relies on a native backend and so its necessary to intercept calls from
-     * fake usage hitting them.
-     * <p>
-     * Mocks don't work directly on native calls, but if they're overridden then it does work.
-     * Some basic state faking is also done to make the mocks more realistic.
-     */
-    public static class TestMediaPlayer extends MediaPlayer {
-        private boolean mIsPlaying = false;
-        private boolean mIsLooping = false;
-
-        @Override
-        public void start() {
-            mIsPlaying = true;
-        }
-
-        @Override
-        public void stop() {
-            mIsPlaying = false;
-        }
-
-        @Override
-        public void setLooping(boolean value) {
-            mIsLooping = value;
-        }
-
-        @Override
-        public boolean isLooping() {
-            return mIsLooping;
-        }
-
-        @Override
-        public boolean isPlaying() {
-            return mIsPlaying;
-        }
-
-        /**
-         * Updates {@link #isPlaying()} result to false, if it's set to true.
-         *
-         * @throws IllegalStateException is {@link #isPlaying()} is already false
-         */
-        public void simulatePlayingFinished() {
-            if (!mIsPlaying) {
-                throw new IllegalStateException(
-                        "Attempted to pretend playing finished when not playing");
-            }
-            mIsPlaying = false;
-        }
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java
new file mode 100644
index 0000000..a9fd380
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.testutils.shadow;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.hardware.display.ColorDisplayManager;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(ColorDisplayManager.class)
+public class ShadowColorDisplayManager extends org.robolectric.shadows.ShadowColorDisplayManager {
+
+    private boolean mIsReduceBrightColorsActivated;
+
+    @Implementation
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean setReduceBrightColorsActivated(boolean activated) {
+        mIsReduceBrightColorsActivated = activated;
+        return true;
+    }
+
+    @Implementation
+    @SystemApi
+    public boolean isReduceBrightColorsActivated() {
+        return mIsReduceBrightColorsActivated;
+    }
+
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index ce96bbf..abc62c4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -43,6 +43,7 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.round
 import com.android.compose.animation.scene.transformation.PropertyTransformation
+import com.android.compose.animation.scene.transformation.SharedElementTransformation
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.util.lerp
 
@@ -196,29 +197,44 @@
             state.fromScene == state.toScene ||
             !layoutImpl.isTransitionReady(state) ||
             state.fromScene !in element.sceneValues ||
-            state.toScene !in element.sceneValues ||
-            !isSharedElementEnabled(layoutImpl, state, element.key)
+            state.toScene !in element.sceneValues
     ) {
         return true
     }
 
-    val otherScene =
-        layoutImpl.scenes.getValue(
-            if (scene.key == state.fromScene) {
-                state.toScene
-            } else {
-                state.fromScene
-            }
-        )
-
-    // When the element is shared, draw the one in the highest scene unless it is a background, i.e.
-    // it is usually drawn below everything else.
-    val isHighestScene = scene.zIndex > otherScene.zIndex
-    return if (element.key.isBackground) {
-        !isHighestScene
-    } else {
-        isHighestScene
+    val sharedTransformation = sharedElementTransformation(layoutImpl, state, element.key)
+    if (sharedTransformation?.enabled == false) {
+        return true
     }
+
+    return shouldDrawOrComposeSharedElement(
+        layoutImpl,
+        state,
+        scene.key,
+        element.key,
+        sharedTransformation,
+    )
+}
+
+internal fun shouldDrawOrComposeSharedElement(
+    layoutImpl: SceneTransitionLayoutImpl,
+    transition: TransitionState.Transition,
+    scene: SceneKey,
+    element: ElementKey,
+    sharedTransformation: SharedElementTransformation?
+): Boolean {
+    val scenePicker = sharedTransformation?.scenePicker ?: DefaultSharedElementScenePicker
+    val fromScene = transition.fromScene
+    val toScene = transition.toScene
+
+    return scenePicker.sceneDuringTransition(
+        element = element,
+        fromScene = fromScene,
+        toScene = toScene,
+        progress = transition::progress,
+        fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
+        toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
+    ) == scene
 }
 
 private fun isSharedElementEnabled(
@@ -226,6 +242,14 @@
     transition: TransitionState.Transition,
     element: ElementKey,
 ): Boolean {
+    return sharedElementTransformation(layoutImpl, transition, element)?.enabled ?: true
+}
+
+internal fun sharedElementTransformation(
+    layoutImpl: SceneTransitionLayoutImpl,
+    transition: TransitionState.Transition,
+    element: ElementKey,
+): SharedElementTransformation? {
     val spec = layoutImpl.transitions.transitionSpec(transition.fromScene, transition.toScene)
     val sharedInFromScene = spec.transformations(element, transition.fromScene).shared
     val sharedInToScene = spec.transformations(element, transition.toScene).shared
@@ -238,7 +262,7 @@
         )
     }
 
-    return sharedInFromScene?.enabled ?: true
+    return sharedInFromScene
 }
 
 /**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 6dbeb69..fa385d0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -22,6 +22,8 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.Modifier
@@ -60,7 +62,16 @@
         // which case we still need to draw it.
         val picture = remember { Picture() }
 
-        if (shouldComposeMovableElement(layoutImpl, scene.key, element)) {
+        // Whether we should compose the movable element here. The scene picker logic to know in
+        // which scene we should compose/draw a movable element might depend on the current
+        // transition progress, so we put this in a derivedStateOf to prevent many recompositions
+        // during the transition.
+        val shouldComposeMovableElement by
+            remember(layoutImpl, scene.key, element) {
+                derivedStateOf { shouldComposeMovableElement(layoutImpl, scene.key, element) }
+            }
+
+        if (shouldComposeMovableElement) {
             Box(
                 Modifier.drawWithCache {
                     val width = size.width.toInt()
@@ -172,14 +183,13 @@
         return scene == fromScene
     }
 
-    // If we are ready in both scenes, then compose in the scene that has the highest zIndex (unless
-    // it is a background) given that this is the one that is going to be drawn.
-    val isHighestScene = layoutImpl.scene(scene).zIndex > layoutImpl.scene(otherScene).zIndex
-    return if (element.key.isBackground) {
-        !isHighestScene
-    } else {
-        isHighestScene
-    }
+    return shouldDrawOrComposeSharedElement(
+        layoutImpl,
+        transitionState,
+        scene,
+        element.key,
+        sharedElementTransformation(layoutImpl, transitionState, element.key),
+    )
 }
 
 private class MovableElementScopeImpl(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 4966977..7b7ddfa 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -120,8 +120,14 @@
      *
      * @param enabled whether the matched element(s) should actually be shared in this transition.
      *   Defaults to true.
+     * @param scenePicker the [SharedElementScenePicker] to use when deciding in which scene we
+     *   should draw or compose this shared element.
      */
-    fun sharedElement(matcher: ElementMatcher, enabled: Boolean = true)
+    fun sharedElement(
+        matcher: ElementMatcher,
+        enabled: Boolean = true,
+        scenePicker: SharedElementScenePicker = DefaultSharedElementScenePicker,
+    )
 
     /**
      * Punch a hole in the element(s) matching [matcher] that has the same bounds as [bounds] and
@@ -144,6 +150,44 @@
     fun reversed(builder: TransitionBuilder.() -> Unit)
 }
 
+interface SharedElementScenePicker {
+    /**
+     * Return the scene in which [element] should be drawn (when using `Modifier.element(key)`) or
+     * composed (when using `MovableElement(key)`) during the transition from [fromScene] to
+     * [toScene].
+     */
+    fun sceneDuringTransition(
+        element: ElementKey,
+        fromScene: SceneKey,
+        toScene: SceneKey,
+        progress: () -> Float,
+        fromSceneZIndex: Float,
+        toSceneZIndex: Float,
+    ): SceneKey
+}
+
+object DefaultSharedElementScenePicker : SharedElementScenePicker {
+    override fun sceneDuringTransition(
+        element: ElementKey,
+        fromScene: SceneKey,
+        toScene: SceneKey,
+        progress: () -> Float,
+        fromSceneZIndex: Float,
+        toSceneZIndex: Float
+    ): SceneKey {
+        // By default shared elements are drawn in the highest scene possible, unless it is a
+        // background.
+        return if (
+            (fromSceneZIndex > toSceneZIndex && !element.isBackground) ||
+                (fromSceneZIndex < toSceneZIndex && element.isBackground)
+        ) {
+            fromScene
+        } else {
+            toScene
+        }
+    }
+}
+
 @TransitionDsl
 interface PropertyTransformationBuilder {
     /**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index f1c2717..d2bfd91 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -111,8 +111,12 @@
         range = null
     }
 
-    override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
-        transformations.add(SharedElementTransformation(matcher, enabled))
+    override fun sharedElement(
+        matcher: ElementMatcher,
+        enabled: Boolean,
+        scenePicker: SharedElementScenePicker,
+    ) {
+        transformations.add(SharedElementTransformation(matcher, enabled, scenePicker))
     }
 
     override fun timestampRange(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 2ef8d56..0db8469 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -21,6 +21,7 @@
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.Scene
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.SharedElementScenePicker
 import com.android.compose.animation.scene.TransitionState
 
 /** A transformation applied to one or more elements during a transition. */
@@ -48,6 +49,7 @@
 internal class SharedElementTransformation(
     override val matcher: ElementMatcher,
     internal val enabled: Boolean,
+    internal val scenePicker: SharedElementScenePicker,
 ) : Transformation
 
 /** A transformation that is applied on the element during the whole transition. */
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index 4204cd5..83af630 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -144,7 +144,36 @@
         rule.testTransition(
             fromSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(50.dp)) },
             toSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(100.dp)) },
-            transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
+            transition = {
+                spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+                sharedElement(
+                    TestElements.Foo,
+                    scenePicker =
+                        object : SharedElementScenePicker {
+                            override fun sceneDuringTransition(
+                                element: ElementKey,
+                                fromScene: SceneKey,
+                                toScene: SceneKey,
+                                progress: () -> Float,
+                                fromSceneZIndex: Float,
+                                toSceneZIndex: Float
+                            ): SceneKey {
+                                assertThat(fromScene).isEqualTo(TestScenes.SceneA)
+                                assertThat(toScene).isEqualTo(TestScenes.SceneB)
+                                assertThat(fromSceneZIndex).isEqualTo(0)
+                                assertThat(toSceneZIndex).isEqualTo(1)
+
+                                // Compose Foo in Scene A if progress < 0.65f, otherwise compose it
+                                // in Scene B.
+                                return if (progress() < 0.65f) {
+                                    TestScenes.SceneA
+                                } else {
+                                    TestScenes.SceneB
+                                }
+                            }
+                        }
+                )
+            },
             fromScene = TestScenes.SceneA,
             toScene = TestScenes.SceneB,
         ) {
@@ -170,9 +199,12 @@
 
             at(32) {
                 // During the transition, there is a single counter that is moved, with the current
-                // value.
+                // value. Given that progress = 0.5f, it is currently composed in SceneA.
                 rule
-                    .onNode(hasText("count: 3"))
+                    .onNode(
+                        hasText("count: 3") and
+                            hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+                    )
                     .assertIsDisplayed()
                     .assertSizeIsEqualTo(75.dp, 75.dp)
 
@@ -186,6 +218,26 @@
                     .isEqualTo(1)
             }
 
+            at(48) {
+                // During the transition, there is a single counter that is moved, with the current
+                // value. Given that progress = 0.75f, it is currently composed in SceneB.
+                rule
+                    .onNode(
+                        hasText("count: 3") and
+                            hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+                    )
+                    .assertIsDisplayed()
+
+                // There are no other counters.
+                assertThat(
+                        rule
+                            .onAllNodesWithText("count: ", substring = true)
+                            .fetchSemanticsNodes()
+                            .size
+                    )
+                    .isEqualTo(1)
+            }
+
             after {
                 // At the end of the transition, the counter still has the current value.
                 rule
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 75e71e4..9ac1e9f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -730,6 +730,9 @@
     <!-- Whether the communal service should be enabled -->
     <bool name="config_communalServiceEnabled">false</bool>
 
+    <!-- Component names of allowed communal widgets -->
+    <string-array name="config_communalWidgetAllowlist" translatable="false" />
+
     <!-- Component name of communal source service -->
     <string name="config_communalSourceComponent" translatable="false">@null</string>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 84e06e2..205c297 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3463,7 +3463,8 @@
     @Deprecated
     private boolean isUnlockWithFacePossible(int userId) {
         if (isFaceAuthInteractorEnabled()) {
-            return getFaceAuthInteractor().canFaceAuthRun();
+            return getFaceAuthInteractor() != null
+                    && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled();
         }
         return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
new file mode 100644
index 0000000..f9c4f29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.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.communal.data.model
+
+import com.android.systemui.communal.shared.CommunalContentSize
+
+/** Metadata for the default widgets */
+data class CommunalWidgetMetadata(
+    /* Widget provider component name */
+    val componentName: String,
+
+    /* Defines the order in which the widget will be rendered in the grid. */
+    val priority: Int,
+
+    /* Supported sizes */
+    val sizes: List<CommunalContentSize>
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e2a7d07..f13b62f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -27,13 +27,17 @@
 import android.os.UserManager
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
 import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.CommunalContentSize
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
@@ -45,15 +49,20 @@
 interface CommunalWidgetRepository {
     /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
     val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
+
+    /** Widgets that are allowed to render in the glanceable hub */
+    val communalWidgetAllowlist: List<CommunalWidgetMetadata>
 }
 
 @SysUISingleton
 class CommunalWidgetRepositoryImpl
 @Inject
 constructor(
+    @Application private val applicationContext: Context,
     private val appWidgetManager: AppWidgetManager,
     private val appWidgetHost: AppWidgetHost,
     broadcastDispatcher: BroadcastDispatcher,
+    communalRepository: CommunalRepository,
     private val packageManager: PackageManager,
     private val userManager: UserManager,
     private val userTracker: UserTracker,
@@ -64,12 +73,18 @@
         const val TAG = "CommunalWidgetRepository"
         const val WIDGET_LABEL = "Stopwatch"
     }
+    override val communalWidgetAllowlist: List<CommunalWidgetMetadata>
 
     private val logger = Logger(logBuffer, TAG)
 
     // Whether the [AppWidgetHost] is listening for updates.
     private var isHostListening = false
 
+    init {
+        communalWidgetAllowlist =
+            if (communalRepository.isCommunalEnabled) getWidgetAllowlist() else emptyList()
+    }
+
     // Widgets that should be rendered in communal mode.
     private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
 
@@ -129,6 +144,18 @@
             return@map addWidget(providerInfo)
         }
 
+    private fun getWidgetAllowlist(): List<CommunalWidgetMetadata> {
+        val componentNames =
+            applicationContext.resources.getStringArray(R.array.config_communalWidgetAllowlist)
+        return componentNames.mapIndexed { index, name ->
+            CommunalWidgetMetadata(
+                componentName = name,
+                priority = componentNames.size - index,
+                sizes = listOf(CommunalContentSize.HALF)
+            )
+        }
+    }
+
     private fun startListening() {
         if (isHostListening) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
new file mode 100644
index 0000000..0bd7d86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
@@ -0,0 +1,8 @@
+package com.android.systemui.communal.shared
+
+/** Supported sizes for communal content in the layout grid. */
+enum class CommunalContentSize {
+    FULL,
+    HALF,
+    THIRD,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index e7f835f..c3aaef7 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.deviceentry
 
+import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepositoryModule
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
 import dagger.Module
 
@@ -7,6 +8,7 @@
     includes =
         [
             DeviceEntryRepositoryModule::class,
+            DeviceEntryHapticsRepositoryModule::class,
         ],
 )
 object DeviceEntryModule
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt
new file mode 100644
index 0000000..1458404
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.deviceentry.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Interface for classes that can access device-entry haptics application state. */
+interface DeviceEntryHapticsRepository {
+    /**
+     * Whether a successful biometric haptic has been requested. Has not yet been handled if true.
+     */
+    val successHapticRequest: Flow<Boolean>
+
+    /** Whether an error biometric haptic has been requested. Has not yet been handled if true. */
+    val errorHapticRequest: Flow<Boolean>
+
+    fun requestSuccessHaptic()
+    fun handleSuccessHaptic()
+    fun requestErrorHaptic()
+    fun handleErrorHaptic()
+}
+
+/** Encapsulates application state for device entry haptics. */
+@SysUISingleton
+class DeviceEntryHapticsRepositoryImpl @Inject constructor() : DeviceEntryHapticsRepository {
+    private val _successHapticRequest = MutableStateFlow(false)
+    override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow()
+
+    private val _errorHapticRequest = MutableStateFlow(false)
+    override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow()
+
+    override fun requestSuccessHaptic() {
+        _successHapticRequest.value = true
+    }
+
+    override fun handleSuccessHaptic() {
+        _successHapticRequest.value = false
+    }
+
+    override fun requestErrorHaptic() {
+        _errorHapticRequest.value = true
+    }
+
+    override fun handleErrorHaptic() {
+        _errorHapticRequest.value = false
+    }
+}
+
+@Module
+interface DeviceEntryHapticsRepositoryModule {
+    @Binds fun repository(impl: DeviceEntryHapticsRepositoryImpl): DeviceEntryHapticsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
new file mode 100644
index 0000000..53d6f73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.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.deviceentry.domain.interactor
+
+import com.android.keyguard.logging.BiometricUnlockLogger
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepository
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.combineTransform
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Business logic for device entry haptic events. Determines whether the haptic should play. In
+ * particular, there are extra guards for whether device entry error and successes hatpics should
+ * play when the physical fingerprint sensor is located on the power button.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntryHapticsInteractor
+@Inject
+constructor(
+    private val repository: DeviceEntryHapticsRepository,
+    fingerprintPropertyRepository: FingerprintPropertyRepository,
+    biometricSettingsRepository: BiometricSettingsRepository,
+    keyEventInteractor: KeyEventInteractor,
+    powerInteractor: PowerInteractor,
+    private val systemClock: SystemClock,
+    private val logger: BiometricUnlockLogger,
+) {
+    private val powerButtonSideFpsEnrolled =
+        combineTransform(
+                fingerprintPropertyRepository.sensorType,
+                biometricSettingsRepository.isFingerprintEnrolledAndEnabled,
+            ) { sensorType, enrolledAndEnabled ->
+                if (sensorType == FingerprintSensorType.POWER_BUTTON) {
+                    emit(enrolledAndEnabled)
+                } else {
+                    emit(false)
+                }
+            }
+            .distinctUntilChanged()
+    private val powerButtonDown: Flow<Boolean> = keyEventInteractor.isPowerButtonDown
+    private val lastPowerButtonWakeup: Flow<Long> =
+        powerInteractor.detailedWakefulness
+            .filter { it.isAwakeFrom(WakeSleepReason.POWER_BUTTON) }
+            .map { systemClock.uptimeMillis() }
+            .onStart {
+                // If the power button hasn't been pressed, we still want this to evaluate to true:
+                // `uptimeMillis() - lastPowerButtonWakeup > recentPowerButtonPressThresholdMs`
+                emit(recentPowerButtonPressThresholdMs * -1L - 1L)
+            }
+
+    val playSuccessHaptic: Flow<Boolean> =
+        repository.successHapticRequest
+            .filter { it }
+            .sample(
+                combine(
+                    powerButtonSideFpsEnrolled,
+                    powerButtonDown,
+                    lastPowerButtonWakeup,
+                    ::Triple
+                )
+            )
+            .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
+                val sideFpsAllowsHaptic =
+                    !powerButtonDown &&
+                        systemClock.uptimeMillis() - lastPowerButtonWakeup >
+                            recentPowerButtonPressThresholdMs
+                val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic
+                if (!allowHaptic) {
+                    logger.d("Skip success haptic. Recent power button press or button is down.")
+                    handleSuccessHaptic() // immediately handle, don't vibrate
+                }
+                allowHaptic
+            }
+    val playErrorHaptic: Flow<Boolean> =
+        repository.errorHapticRequest
+            .filter { it }
+            .sample(combine(powerButtonSideFpsEnrolled, powerButtonDown, ::Pair))
+            .map { (sideFpsEnrolled, powerButtonDown) ->
+                val allowHaptic = !sideFpsEnrolled || !powerButtonDown
+                if (!allowHaptic) {
+                    logger.d("Skip error haptic. Power button is down.")
+                    handleErrorHaptic() // immediately handle, don't vibrate
+                }
+                allowHaptic
+            }
+
+    fun vibrateSuccess() {
+        repository.requestSuccessHaptic()
+    }
+
+    fun vibrateError() {
+        repository.requestErrorHaptic()
+    }
+
+    fun handleSuccessHaptic() {
+        repository.handleSuccessHaptic()
+    }
+
+    fun handleErrorHaptic() {
+        repository.handleErrorHaptic()
+    }
+
+    private val recentPowerButtonPressThresholdMs = 400L
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4764f22..472cc24 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -259,7 +259,7 @@
     // TODO(b/290652751): Tracking bug.
     @JvmField
     val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
-        unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
+        releasedFlag("migrate_split_keyguard_bottom_area")
 
     // TODO(b/297037052): Tracking bug.
     @JvmField
@@ -274,7 +274,7 @@
 
     /** Migrate the lock icon view to the new keyguard root view. */
     // TODO(b/286552209): Tracking bug.
-    @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon", teamfood = true)
+    @JvmField val MIGRATE_LOCK_ICON = releasedFlag("migrate_lock_icon")
 
     // TODO(b/288276738): Tracking bug.
     @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -419,7 +419,7 @@
         releasedFlag("incompatible_charging_battery_icon")
 
     // TODO(b/293585143): Tracking Bug
-    val INSTANT_TETHER = unreleasedFlag("instant_tether")
+    val INSTANT_TETHER = unreleasedFlag("instant_tether", teamfood = true)
 
     // TODO(b/294588085): Tracking Bug
     val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
@@ -503,10 +503,9 @@
     @Keep
     @JvmField
     val WM_ENABLE_PARTIAL_SCREEN_SHARING =
-        unreleasedFlag(
-            name = "record_task_content",
+        releasedFlag(
+            name = "enable_record_task_content",
             namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-            teamfood = true
         )
 
     // TODO(b/254512674): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
index 20d99d1..7b33e11 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -32,6 +32,8 @@
     @FloatRange(from = 0.0, to = 1.0) val additionalVelocityMaxBump: Float = 0.15f,
     /** Additional time delta to wait between drag texture vibrations */
     @FloatRange(from = 0.0) val deltaMillisForDragInterval: Float = 0f,
+    /** Progress threshold beyond which a new drag texture is delivered */
+    @FloatRange(from = 0.0, to = 1.0) val deltaProgressForDragThreshold: Float = 0.015f,
     /** Number of low ticks in a drag texture composition. This is not expected to change */
     val numberOfLowTicks: Int = 5,
     /** Maximum velocity allowed for vibration scaling. This is not expected to change. */
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
index e6de156..f313fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -46,6 +46,8 @@
     private val positionAccelerateInterpolator =
         AccelerateInterpolator(config.progressInterpolatorFactor)
     private var dragTextureLastTime = clock.elapsedRealtime()
+    var dragTextureLastProgress = -1f
+        private set
     private val lowTickDurationMs =
         vibratorHelper.getPrimitiveDurations(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)[0]
     private var hasVibratedAtLowerBookend = false
@@ -91,6 +93,9 @@
         val elapsedSinceLastDrag = currentTime - dragTextureLastTime
         if (elapsedSinceLastDrag < thresholdUntilNextDragCallMillis) return
 
+        val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress)
+        if (deltaProgress < config.deltaProgressForDragThreshold) return
+
         val velocityInterpolated =
             velocityAccelerateInterpolator.getInterpolation(
                 min(absoluteVelocity / config.maxVelocityToScale, 1f)
@@ -116,11 +121,14 @@
         }
         vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING)
         dragTextureLastTime = currentTime
+        dragTextureLastProgress = normalizedSliderProgress
     }
 
     override fun onHandleAcquiredByTouch() {}
 
-    override fun onHandleReleasedFromTouch() {}
+    override fun onHandleReleasedFromTouch() {
+        dragTextureLastProgress = -1f
+    }
 
     override fun onLowerBookend() {
         if (!hasVibratedAtLowerBookend) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index a511713..119ade4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -28,6 +28,7 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
@@ -44,6 +45,7 @@
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import javax.inject.Inject
@@ -72,7 +74,9 @@
     private val keyguardIndicationController: KeyguardIndicationController,
     private val lockIconViewController: LockIconViewController,
     private val shadeInteractor: ShadeInteractor,
-    private val interactionJankMonitor: InteractionJankMonitor
+    private val interactionJankMonitor: InteractionJankMonitor,
+    private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
+    private val vibratorHelper: VibratorHelper,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -143,6 +147,8 @@
                 shadeInteractor,
                 { keyguardStatusViewController!!.getClockController() },
                 interactionJankMonitor,
+                deviceEntryHapticsInteractor,
+                vibratorHelper,
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index e8740a4..654f2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -90,7 +90,7 @@
      * If the current user can use face auth to enter the device. This is true when the user has
      * face auth enrolled, and is enabled in settings/device policy.
      */
-    val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
+    val isFaceAuthEnrolledAndEnabled: StateFlow<Boolean>
 
     /**
      * If the current user can use face auth to enter the device right now. This is true when
@@ -348,10 +348,11 @@
             .and(isFingerprintBiometricAllowed)
             .stateIn(scope, SharingStarted.Eagerly, false)
 
-    override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> =
+    override val isFaceAuthEnrolledAndEnabled: StateFlow<Boolean> =
         isFaceAuthenticationEnabled
             .and(isFaceEnrolled)
             .and(mobileConnectionsRepository.isAnySimSecure.isFalse())
+            .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isFaceAuthCurrentlyAllowed: Flow<Boolean> =
         isFaceAuthEnrolledAndEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index 89aca76..85b0f4fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -41,6 +41,9 @@
     /** Whether face auth is in lock out state. */
     fun isLockedOut(): Boolean
 
+    /** Whether face auth is enrolled and enabled for the current user */
+    fun isFaceAuthEnabledAndEnrolled(): Boolean
+
     /**
      * Register listener for use from code that cannot use [authenticationStatus] or
      * [detectionStatus]
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index f38bb2b..fbadde6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -43,6 +43,7 @@
     override fun isLockedOut(): Boolean = false
 
     override fun isEnabled() = false
+    override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
 
     override fun registerListener(listener: FaceAuthenticationListener) {}
 
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 797dec2..2641846 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
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
@@ -81,6 +82,7 @@
     private val facePropertyRepository: FacePropertyRepository,
     private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig,
     private val powerInteractor: PowerInteractor,
+    private val biometricSettingsRepository: BiometricSettingsRepository,
 ) : CoreStartable, KeyguardFaceAuthInteractor {
 
     private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -149,7 +151,10 @@
             .onEach {
                 if (it) {
                     faceAuthenticationLogger.faceLockedOut("Fingerprint locked out")
-                    repository.setLockedOut(true)
+                    // We don't care about this if face auth is not enabled.
+                    if (isFaceAuthEnabledAndEnrolled()) {
+                        repository.setLockedOut(true)
+                    }
                 }
             }
             .launchIn(applicationScope)
@@ -263,6 +268,9 @@
         }
     }
 
+    override fun isFaceAuthEnabledAndEnrolled(): Boolean =
+        biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.value
+
     private fun observeFaceAuthStateUpdates() {
         authenticationStatus
             .onEach { authStatusUpdate ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index c72e6ce..4d5c503 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.binder
 
 import android.annotation.DrawableRes
+import android.view.HapticFeedbackConstants
 import android.view.View
 import android.view.View.OnLayoutChangeListener
 import android.view.ViewGroup
@@ -29,6 +30,7 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -38,6 +40,7 @@
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.temporarydisplay.ViewPriority
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -45,6 +48,7 @@
 import javax.inject.Provider
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
 
 /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@@ -62,6 +66,8 @@
         shadeInteractor: ShadeInteractor,
         clockControllerProvider: Provider<ClockController>?,
         interactionJankMonitor: InteractionJankMonitor?,
+        deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?,
+        vibratorHelper: VibratorHelper?,
     ): DisposableHandle {
         var onLayoutChangeListener: OnLayoutChange? = null
         val childViews = mutableMapOf<Int, View?>()
@@ -177,6 +183,44 @@
                                 }
                         }
                     }
+
+                    if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
+                        launch {
+                            deviceEntryHapticsInteractor.playSuccessHaptic
+                                .filter { it }
+                                .collect {
+                                    if (
+                                        featureFlags.isEnabled(Flags.ONE_WAY_HAPTICS_API_MIGRATION)
+                                    ) {
+                                        vibratorHelper.performHapticFeedback(
+                                            view,
+                                            HapticFeedbackConstants.CONFIRM,
+                                        )
+                                    } else {
+                                        vibratorHelper.vibrateAuthSuccess("device-entry::success")
+                                    }
+                                    deviceEntryHapticsInteractor.handleSuccessHaptic()
+                                }
+                        }
+
+                        launch {
+                            deviceEntryHapticsInteractor.playErrorHaptic
+                                .filter { it }
+                                .collect {
+                                    if (
+                                        featureFlags.isEnabled(Flags.ONE_WAY_HAPTICS_API_MIGRATION)
+                                    ) {
+                                        vibratorHelper.performHapticFeedback(
+                                            view,
+                                            HapticFeedbackConstants.REJECT,
+                                        )
+                                    } else {
+                                        vibratorHelper.vibrateAuthSuccess("device-entry::error")
+                                    }
+                                    deviceEntryHapticsInteractor.handleErrorHaptic()
+                                }
+                        }
+                    }
                 }
             }
         viewModel.clockControllerProvider = clockControllerProvider
@@ -189,7 +233,7 @@
         view.setOnHierarchyChangeListener(
             object : OnHierarchyChangeListener {
                 override fun onChildViewAdded(parent: View, child: View) {
-                    childViews.put(child.id, view)
+                    childViews.put(child.id, child)
                 }
 
                 override fun onChildViewRemoved(parent: View, child: View) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index c1c29c4..692984a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -27,6 +27,7 @@
 import android.os.Bundle
 import android.os.Handler
 import android.os.IBinder
+import android.view.ContextThemeWrapper
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.DisplayInfo
@@ -45,6 +46,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
@@ -113,6 +115,7 @@
     private val chipbarCoordinator: ChipbarCoordinator,
     private val keyguardStateController: KeyguardStateController,
     private val shadeInteractor: ShadeInteractor,
+    private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
 ) {
 
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -179,7 +182,11 @@
 
     fun render() {
         mainHandler.post {
-            val previewContext = display?.let { context.createDisplayContext(it) } ?: context
+            val previewContext =
+                display?.let {
+                    ContextThemeWrapper(context.createDisplayContext(it), context.getTheme())
+                }
+                    ?: context
 
             val rootView = FrameLayout(previewContext)
 
@@ -334,6 +341,8 @@
                 shadeInteractor,
                 null, // clock provider only needed for burn in
                 null, // jank monitor not required for preview mode
+                null, // device entry haptics not required for preview mode
+                null, // device entry haptics not required for preview mode
             )
         )
         rootView.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index 9371d4e..342a440 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -50,11 +50,8 @@
 
     override fun addViews(constraintLayout: ConstraintLayout) {
         if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
-            val view =
-                LayoutInflater.from(constraintLayout.context)
-                    .inflate(R.layout.ambient_indication, constraintLayout, false)
-
-            constraintLayout.addView(view)
+            LayoutInflater.from(constraintLayout.context)
+                .inflate(R.layout.ambient_indication, constraintLayout, true)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index 8634b09..a53f0f1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -19,7 +19,11 @@
 import android.os.Process
 import android.os.RemoteException
 import android.util.Log
-import com.android.internal.util.FrameworkStatsLog
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED
 import com.android.systemui.dagger.SysUISingleton
 import javax.inject.Inject
 
@@ -36,21 +40,23 @@
      *
      * @param sessionCreationSource The entry point requesting permission to capture.
      */
-    fun notifyPermissionProgress(state: Int, sessionCreationSource: Int) {
-        // TODO check that state & SessionCreationSource matches expected values
-        notifyToServer(state, sessionCreationSource)
+    fun notifyProjectionInitiated(sessionCreationSource: SessionCreationSource) {
+        notifyToServer(
+            MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
+            sessionCreationSource
+        )
     }
 
     /**
      * Request to log that the permission request moved to the given state.
      *
-     * Should not be used for the initialization state, since that
+     * Should not be used for the initialization state, since that should use {@link
+     * MediaProjectionMetricsLogger#notifyProjectionInitiated(Int)} and pass the
+     * sessionCreationSource.
      */
     fun notifyPermissionProgress(state: Int) {
         // TODO validate state is valid
-        notifyToServer(
-            state,
-            FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN)
+        notifyToServer(state, SessionCreationSource.UNKNOWN)
     }
 
     /**
@@ -64,16 +70,21 @@
      *   Indicates the entry point for requesting the permission. Must be a valid state defined in
      *   the SessionCreationSource enum.
      */
-    private fun notifyToServer(state: Int, sessionCreationSource: Int) {
+    private fun notifyToServer(state: Int, sessionCreationSource: SessionCreationSource) {
         Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
         try {
             service.notifyPermissionRequestStateChange(
-                Process.myUid(), state, sessionCreationSource)
+                Process.myUid(),
+                state,
+                sessionCreationSource.toMetricsConstant()
+            )
         } catch (e: RemoteException) {
             Log.e(
                 TAG,
-                "Error notifying server of permission flow state $state from source $sessionCreationSource",
-                e)
+                "Error notifying server of permission flow state $state from source " +
+                    "$sessionCreationSource",
+                e
+            )
         }
     }
 
@@ -81,3 +92,18 @@
         const val TAG = "MediaProjectionMetricsLogger"
     }
 }
+
+enum class SessionCreationSource {
+    APP,
+    CAST,
+    SYSTEM_UI_SCREEN_RECORDER,
+    UNKNOWN;
+
+    fun toMetricsConstant(): Int =
+        when (this) {
+            APP -> METRICS_CREATION_SOURCE_APP
+            CAST -> METRICS_CREATION_SOURCE_CAST
+            SYSTEM_UI_SCREEN_RECORDER -> METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+            UNKNOWN -> METRICS_CREATION_SOURCE_UNKNOWN
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index b5d3e91..0bbcfd9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -87,14 +87,15 @@
 
     override fun getLayoutResource() = R.layout.media_projection_app_selector
 
-    public override fun onCreate(bundle: Bundle?) {
+    public override fun onCreate(savedInstanceState: Bundle?) {
         lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
         component =
             componentFactory.create(
                 hostUserHandle = hostUserHandle,
                 callingPackage = callingPackage,
                 view = this,
-                resultHandler = this
+                resultHandler = this,
+                isFirstStart = savedInstanceState == null
             )
         component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
 
@@ -113,7 +114,7 @@
         reviewGrantedConsentRequired =
             intent.getBooleanExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, false)
 
-        super.onCreate(bundle)
+        super.onCreate(savedInstanceState)
         controller.init()
         // we override AppList's AccessibilityDelegate set in ResolverActivity.onCreate because in
         // our case this delegate must extend RecyclerViewAccessibilityDelegate, otherwise
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 2217509..8c6f307 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -146,6 +146,9 @@
             @BindsInstance @MediaProjectionAppSelector callingPackage: String?,
             @BindsInstance view: MediaProjectionAppSelectorView,
             @BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
+            // Whether the app selector is starting for the first time. False when it is re-starting
+            // due to a config change.
+            @BindsInstance @MediaProjectionAppSelector isFirstStart: Boolean,
         ): MediaProjectionAppSelectorComponent
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index fced117..69132d3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -18,6 +18,8 @@
 
 import android.content.ComponentName
 import android.os.UserHandle
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
@@ -43,9 +45,17 @@
     @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName,
     @MediaProjectionAppSelector private val callerPackageName: String?,
     private val thumbnailLoader: RecentTaskThumbnailLoader,
+    @MediaProjectionAppSelector private val isFirstStart: Boolean,
+    private val logger: MediaProjectionMetricsLogger,
 ) {
 
     fun init() {
+        // Only log during the first start of the app selector.
+        // Don't log when the app selector restarts due to a config change.
+        if (isFirstStart) {
+            logger.notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+        }
+
         scope.launch {
             val recentTasks = recentTaskListProvider.loadRecentTasks()
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index a9e6c53..e9b4582 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -22,6 +22,7 @@
 
 data class RecentTask(
     val taskId: Int,
+    val displayId: Int,
     @UserIdInt val userId: Int,
     val topActivityComponent: ComponentName?,
     val baseIntentComponent: ComponentName?,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index aa4c4e5..730aa62 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -60,6 +60,7 @@
                 .map {
                     RecentTask(
                         it.taskId,
+                        it.displayId,
                         it.userId,
                         it.topActivity,
                         it.baseIntent?.component,
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 fd1a683..ba837db 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
@@ -130,10 +130,10 @@
                 view.width,
                 view.height
             )
-        activityOptions.setPendingIntentBackgroundActivityStartMode(
+        activityOptions.pendingIntentBackgroundActivityStartMode =
             MODE_BACKGROUND_ACTIVITY_START_ALLOWED
-        )
         activityOptions.launchCookie = launchCookie
+        activityOptions.launchDisplayId = task.displayId
 
         activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle())
         resultHandler.returnSelectedApp(launchCookie)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index d08d040..fa418fc 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -53,7 +53,9 @@
 
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
 import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
+import com.android.systemui.mediaprojection.SessionCreationSource;
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
@@ -74,6 +76,7 @@
     private final FeatureFlags mFeatureFlags;
     private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
     private final StatusBarManager mStatusBarManager;
+    private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
 
     private String mPackageName;
     private int mUid;
@@ -90,15 +93,17 @@
     @Inject
     public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
             Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
-            StatusBarManager statusBarManager) {
+            StatusBarManager statusBarManager,
+            MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
         mFeatureFlags = featureFlags;
         mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
         mStatusBarManager = statusBarManager;
+        mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
     }
 
     @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
 
         final Intent launchingIntent = getIntent();
         mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
@@ -133,6 +138,10 @@
 
         try {
             if (MediaProjectionServiceHelper.hasProjectionPermission(mUid, mPackageName)) {
+                if (savedInstanceState == null) {
+                    mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+                            SessionCreationSource.APP);
+                }
                 final IMediaProjection projection =
                         MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
                                 mReviewGrantedConsentRequired);
@@ -231,6 +240,13 @@
             mDialog = dialogBuilder.create();
         }
 
+        if (savedInstanceState == null) {
+            mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+                    appName == null
+                            ? SessionCreationSource.CAST
+                            : SessionCreationSource.APP);
+        }
+
         setUpDialog(mDialog);
         mDialog.show();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index c4749e0..c77f3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -231,7 +231,6 @@
             animation.removeEndListener(this)
 
             if (!canceled) {
-
                 // The delay between finishing this animation and starting the runnable
                 val delay = max(0, runnableDelay - elapsedTimeSinceEntry)
 
@@ -461,7 +460,6 @@
     }
 
     private fun handleMoveEvent(event: MotionEvent) {
-
         val x = event.x
         val y = event.y
 
@@ -927,17 +925,7 @@
             GestureState.ACTIVE -> {
                 previousXTranslationOnActiveOffset = previousXTranslation
                 updateRestingArrowDimens()
-                if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-                    vibratorHelper.performHapticFeedback(
-                        mView,
-                        HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
-                    )
-                } else {
-                    vibratorHelper.cancel()
-                    mainHandler.postDelayed(10L) {
-                        vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
-                    }
-                }
+                performActivatedHapticFeedback()
                 val popVelocity =
                     if (previousState == GestureState.INACTIVE) {
                         POP_ON_INACTIVE_TO_ACTIVE_VELOCITY
@@ -958,25 +946,24 @@
 
                 mView.popOffEdge(POP_ON_INACTIVE_VELOCITY)
 
-                if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-                    vibratorHelper.performHapticFeedback(
-                        mView,
-                        HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
-                    )
-                } else {
-                    vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
-                }
+                performDeactivatedHapticFeedback()
                 updateRestingArrowDimens()
             }
             GestureState.FLUNG -> {
+                // Typically a vibration is only played while transitioning to ACTIVE. However there
+                // are instances where a fling to trigger back occurs while not in that state.
+                // (e.g. A fling is detected before crossing the trigger threshold.)
+                if (previousState != GestureState.ACTIVE) {
+                    performActivatedHapticFeedback()
+                }
                 mainHandler.postDelayed(POP_ON_FLING_DELAY) {
                     mView.popScale(POP_ON_FLING_VELOCITY)
                 }
-                updateRestingArrowDimens()
                 mainHandler.postDelayed(
                     onEndSetCommittedStateListener.runnable,
                     MIN_DURATION_FLING_ANIMATION
                 )
+                updateRestingArrowDimens()
             }
             GestureState.COMMITTED -> {
                 // In most cases, animating between states is handled via `updateRestingArrowDimens`
@@ -1011,6 +998,31 @@
         }
     }
 
+    private fun performDeactivatedHapticFeedback() {
+        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+            vibratorHelper.performHapticFeedback(
+                    mView,
+                    HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
+            )
+        } else {
+            vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
+        }
+    }
+
+    private fun performActivatedHapticFeedback() {
+        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+            vibratorHelper.performHapticFeedback(
+                    mView,
+                    HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
+            )
+        } else {
+            vibratorHelper.cancel()
+            mainHandler.postDelayed(10L) {
+                vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
+            }
+        }
+    }
+
     private fun convertVelocityToAnimationFactor(
         valueOnFastVelocity: Float,
         valueOnSlowVelocity: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 733383e..58c4f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -96,43 +96,9 @@
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
-/** Functions that help creating the People tile layouts. */
-public class PeopleTileViewHelper {
-    /** Turns on debugging information about People Space. */
-    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
-    private static final String TAG = "PeopleTileView";
-
-    private static final int DAYS_IN_A_WEEK = 7;
-    private static final int ONE_DAY = 1;
-
-    public static final int LAYOUT_SMALL = 0;
-    public static final int LAYOUT_MEDIUM = 1;
-    public static final int LAYOUT_LARGE = 2;
-
-    private static final int MIN_CONTENT_MAX_LINES = 2;
-    private static final int NAME_MAX_LINES_WITHOUT_LAST_INTERACTION = 3;
-    private static final int NAME_MAX_LINES_WITH_LAST_INTERACTION = 1;
-
-    private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_NOTIF_CONTENT = 16 + 22 + 8 + 16;
-    private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_STATUS_CONTENT = 16 + 16 + 24 + 4 + 16;
-    private static final int MIN_MEDIUM_VERTICAL_PADDING = 4;
-    private static final int MAX_MEDIUM_PADDING = 16;
-    private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING = 8 + 4;
-    private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_VERTICAL = 6 + 4 + 8;
-    private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_VERTICAL = 4 + 4;
-    private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_HORIZONTAL = 6 + 4;
-    private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_HORIZONTAL = 8 + 8;
-
-    private static final int MESSAGES_COUNT_OVERFLOW = 6;
-
-    private static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
-
-    private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
-    private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
-    private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
-    private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
-
-    static final String BRIEF_PAUSE_ON_TALKBACK = "\n\n";
+/** Variables and functions that is related to Emoji. */
+class EmojiHelper {
+    static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
 
     // This regex can be used to match Unicode emoji characters and character sequences. It's from
     // the official Unicode site (https://unicode.org/reports/tr51/#EBNF_and_Regex) with minor
@@ -165,7 +131,44 @@
                     + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
                     + "?)*";
 
-    private static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
+    static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
+}
+
+/** Functions that help creating the People tile layouts. */
+public class PeopleTileViewHelper {
+    /** Turns on debugging information about People Space. */
+    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+    private static final String TAG = "PeopleTileView";
+
+    private static final int DAYS_IN_A_WEEK = 7;
+    private static final int ONE_DAY = 1;
+
+    public static final int LAYOUT_SMALL = 0;
+    public static final int LAYOUT_MEDIUM = 1;
+    public static final int LAYOUT_LARGE = 2;
+
+    private static final int MIN_CONTENT_MAX_LINES = 2;
+    private static final int NAME_MAX_LINES_WITHOUT_LAST_INTERACTION = 3;
+    private static final int NAME_MAX_LINES_WITH_LAST_INTERACTION = 1;
+
+    private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_NOTIF_CONTENT = 16 + 22 + 8 + 16;
+    private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_STATUS_CONTENT = 16 + 16 + 24 + 4 + 16;
+    private static final int MIN_MEDIUM_VERTICAL_PADDING = 4;
+    private static final int MAX_MEDIUM_PADDING = 16;
+    private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING = 8 + 4;
+    private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_VERTICAL = 6 + 4 + 8;
+    private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_VERTICAL = 4 + 4;
+    private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL_HORIZONTAL = 6 + 4;
+    private static final int FIXED_WIDTH_DIMENS_FOR_SMALL_HORIZONTAL = 8 + 8;
+
+    private static final int MESSAGES_COUNT_OVERFLOW = 6;
+
+    private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
+    private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
+    private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
+    private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
+
+    static final String BRIEF_PAUSE_ON_TALKBACK = "\n\n";
 
     public static final String EMPTY_STRING = "";
 
@@ -831,7 +834,7 @@
 
         if (status.getActivity() == ACTIVITY_BIRTHDAY
                 || status.getActivity() == ACTIVITY_UPCOMING_BIRTHDAY) {
-            setEmojiBackground(views, EMOJI_CAKE);
+            setEmojiBackground(views, EmojiHelper.EMOJI_CAKE);
         }
 
         Icon statusIcon = status.getIcon();
@@ -1072,7 +1075,7 @@
     /** Returns emoji if {@code message} has two of the same emoji in sequence. */
     @VisibleForTesting
     CharSequence getDoubleEmoji(CharSequence message) {
-        Matcher unicodeEmojiMatcher = EMOJI_PATTERN.matcher(message);
+        Matcher unicodeEmojiMatcher = EmojiHelper.EMOJI_PATTERN.matcher(message);
         // Stores the start and end indices of each matched emoji.
         List<Pair<Integer, Integer>> emojiIndices = new ArrayList<>();
         // Stores each emoji text.
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 7a0c087..f469c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -38,6 +38,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
+import com.android.systemui.mediaprojection.SessionCreationSource;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
 import com.android.systemui.plugins.ActivityStarter;
@@ -45,13 +47,13 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.CallbackController;
 
+import dagger.Lazy;
+
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Helper class to initiate a screen recording
  */
@@ -71,6 +73,7 @@
     private final FeatureFlags mFlags;
     private final UserContextProvider mUserContextProvider;
     private final UserTracker mUserTracker;
+    private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
 
     protected static final String INTENT_UPDATE_STATE =
             "com.android.systemui.screenrecord.UPDATE_STATE";
@@ -115,7 +118,8 @@
             FeatureFlags flags,
             UserContextProvider userContextProvider,
             Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
         mMainExecutor = mainExecutor;
         mContext = context;
         mFlags = flags;
@@ -123,6 +127,7 @@
         mBroadcastDispatcher = broadcastDispatcher;
         mUserContextProvider = userContextProvider;
         mUserTracker = userTracker;
+        mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
 
         BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setInteractive(true);
@@ -149,6 +154,9 @@
             return new ScreenCaptureDisabledDialog(mContext);
         }
 
+        mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+                SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+
         return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
                 ? new ScreenRecordPermissionDialog(context,  getHostUserHandle(), this,
                     activityStarter, mUserContextProvider, onStartRecordingClicked)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 6783afa..1ecb127 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
 
 import android.app.Activity;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.Gravity;
@@ -35,8 +36,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -74,21 +75,26 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setWindowAttributes();
+        setContentView(R.layout.brightness_mirror_container);
+        setBrightnessDialogViewAttributes();
+    }
 
+    private void setWindowAttributes() {
         final Window window = getWindow();
 
-        window.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+        window.setGravity(Gravity.TOP | Gravity.LEFT);
         window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
         window.requestFeature(Window.FEATURE_NO_TITLE);
 
         // Calling this creates the decor View, so setLayout takes proper effect
         // (see Dialog#onWindowAttributesChanged)
         window.getDecorView();
-        window.setLayout(
-                WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
+        window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
         getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
+    }
 
-        setContentView(R.layout.brightness_mirror_container);
+    void setBrightnessDialogViewAttributes() {
         FrameLayout frame = findViewById(R.id.brightness_mirror_container);
         // The brightness mirror container is INVISIBLE by default.
         frame.setVisibility(View.VISIBLE);
@@ -97,6 +103,14 @@
                 getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
         lp.leftMargin = horizontalMargin;
         lp.rightMargin = horizontalMargin;
+
+        int verticalMargin =
+                getResources().getDimensionPixelSize(
+                        R.dimen.notification_guts_option_vertical_padding);
+
+        lp.topMargin = verticalMargin;
+        lp.bottomMargin = verticalMargin;
+
         frame.setLayoutParams(lp);
         Rect bounds = new Rect();
         frame.addOnLayoutChangeListener(
@@ -113,6 +127,20 @@
         frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
 
         mBrightnessController = mBrightnessControllerFactory.create(controller);
+
+        Configuration configuration = getResources().getConfiguration();
+        int orientation = configuration.orientation;
+
+        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            lp.width = getWindowManager().getDefaultDisplay().getWidth() / 2
+                    - lp.leftMargin * 2;
+        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            lp.width = getWindowManager().getDefaultDisplay().getWidth()
+                    - lp.leftMargin * 2;
+        }
+
+        frame.setLayoutParams(lp);
+
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
deleted file mode 100644
index 17b4e3b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Point
-import android.graphics.Rect
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicBlur
-import android.util.Log
-import android.util.MathUtils
-import com.android.internal.graphics.ColorUtils
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.MediaNotificationProcessor
-import javax.inject.Inject
-
-private const val TAG = "MediaArtworkProcessor"
-private const val COLOR_ALPHA = (255 * 0.7f).toInt()
-private const val BLUR_RADIUS = 25f
-private const val DOWNSAMPLE = 6
-
-@SysUISingleton
-class MediaArtworkProcessor @Inject constructor() {
-
-    private val mTmpSize = Point()
-    private var mArtworkCache: Bitmap? = null
-
-    fun processArtwork(context: Context, artwork: Bitmap): Bitmap? {
-        if (mArtworkCache != null) {
-            return mArtworkCache
-        }
-        val renderScript = RenderScript.create(context)
-        val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
-        var input: Allocation? = null
-        var output: Allocation? = null
-        var inBitmap: Bitmap? = null
-        try {
-            @Suppress("DEPRECATION")
-            context.display?.getSize(mTmpSize)
-            val rect = Rect(0, 0, artwork.width, artwork.height)
-            MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
-            inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
-                    true /* filter */)
-            // Render script blurs only support ARGB_8888, we need a conversion if we got a
-            // different bitmap config.
-            if (inBitmap.config != Bitmap.Config.ARGB_8888) {
-                val oldIn = inBitmap
-                inBitmap = oldIn.copy(Bitmap.Config.ARGB_8888, false /* isMutable */)
-                oldIn.recycle()
-            }
-            val outBitmap = Bitmap.createBitmap(inBitmap?.width ?: 0, inBitmap?.height ?: 0,
-                    Bitmap.Config.ARGB_8888)
-
-            input = Allocation.createFromBitmap(renderScript, inBitmap,
-                    Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
-            output = Allocation.createFromBitmap(renderScript, outBitmap)
-
-            blur.setRadius(BLUR_RADIUS)
-            blur.setInput(input)
-            blur.forEach(output)
-            output.copyTo(outBitmap)
-
-            val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork)
-
-            val canvas = Canvas(outBitmap)
-            canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA))
-            return outBitmap
-        } catch (ex: IllegalArgumentException) {
-            Log.e(TAG, "error while processing artwork", ex)
-            return null
-        } finally {
-            input?.destroy()
-            output?.destroy()
-            blur.destroy()
-            inBitmap?.recycle()
-        }
-    }
-
-    fun clearCache() {
-        mArtworkCache?.recycle()
-        mArtworkCache = null
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5bd40b8..389486f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,43 +15,29 @@
  */
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_MEDIA_FAKE_ARTWORK;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.ENABLE_LOCKSCREEN_WALLPAPER;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
-
-import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.Point;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.hardware.display.DisplayManager;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
-import android.os.AsyncTask;
 import android.os.Trace;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
 import android.widget.ImageView;
 
-import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.models.player.MediaData;
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
@@ -65,18 +51,11 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ScrimState;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.Utils;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
 
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -85,7 +64,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -99,7 +77,6 @@
     private final StatusBarStateController mStatusBarStateController;
     private final SysuiColorExtractor mColorExtractor;
     private final KeyguardStateController mKeyguardStateController;
-    private final KeyguardBypassController mKeyguardBypassController;
     private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>();
     private static final HashSet<Integer> CONNECTING_MEDIA_STATES = new HashSet<>();
     static {
@@ -117,9 +94,6 @@
     private final NotifCollection mNotifCollection;
 
     @Nullable
-    private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
-
-    @Nullable
     private BiometricUnlockController mBiometricUnlockController;
     @Nullable
     private ScrimController mScrimController;
@@ -128,12 +102,8 @@
     @VisibleForTesting
     boolean mIsLockscreenLiveWallpaperEnabled;
 
-    private final DelayableExecutor mMainExecutor;
-
     private final Context mContext;
     private final ArrayList<MediaListener> mMediaListeners;
-    private final MediaArtworkProcessor mMediaArtworkProcessor;
-    private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
 
     protected NotificationPresenter mPresenter;
     private MediaController mMediaController;
@@ -150,8 +120,6 @@
     private List<String> mSmallerInternalDisplayUids;
     private Display mCurrentDisplay;
 
-    private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
-
     private final MediaController.Callback mMediaListener = new MediaController.Callback() {
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
@@ -173,7 +141,6 @@
             if (DEBUG_MEDIA) {
                 Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
             }
-            mMediaArtworkProcessor.clearCache();
             mMediaMetadata = metadata;
             dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
         }
@@ -184,13 +151,9 @@
      */
     public NotificationMediaManager(
             Context context,
-            Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             NotificationVisibilityProvider visibilityProvider,
-            MediaArtworkProcessor mediaArtworkProcessor,
-            KeyguardBypassController keyguardBypassController,
             NotifPipeline notifPipeline,
             NotifCollection notifCollection,
-            @Main DelayableExecutor mainExecutor,
             MediaDataManager mediaDataManager,
             StatusBarStateController statusBarStateController,
             SysuiColorExtractor colorExtractor,
@@ -199,12 +162,8 @@
             WallpaperManager wallpaperManager,
             DisplayManager displayManager) {
         mContext = context;
-        mMediaArtworkProcessor = mediaArtworkProcessor;
-        mKeyguardBypassController = keyguardBypassController;
         mMediaListeners = new ArrayList<>();
-        mNotificationShadeWindowController = notificationShadeWindowController;
         mVisibilityProvider = visibilityProvider;
-        mMainExecutor = mainExecutor;
         mMediaDataManager = mediaDataManager;
         mNotifPipeline = notifPipeline;
         mNotifCollection = notifCollection;
@@ -476,7 +435,6 @@
     }
 
     private void clearCurrentMediaNotificationSession() {
-        mMediaArtworkProcessor.clearCache();
         mMediaMetadata = null;
         if (mMediaController != null) {
             if (DEBUG_MEDIA) {
@@ -494,9 +452,6 @@
     public void onDisplayUpdated(Display display) {
         Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
         mCurrentDisplay = display;
-        if (mWallapperDrawable != null) {
-            mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
-        }
         Trace.endSection();
     }
 
@@ -531,18 +486,13 @@
     }
 
     /**
-     * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+     * Update media state of lockscreen media views and controllers .
      */
-    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+    public void updateMediaMetaData(boolean metaDataChanged) {
 
         if (mIsLockscreenLiveWallpaperEnabled) return;
 
         Trace.beginSection("CentralSurfaces#updateMediaMetaData");
-        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
-            Trace.endSection();
-            return;
-        }
-
         if (getBackDropView() == null) {
             Trace.endSection();
             return; // called too early
@@ -566,170 +516,14 @@
                     + " state=" + mStatusBarStateController.getState());
         }
 
-        Bitmap artworkBitmap = null;
-        if (mediaMetadata != null && !mKeyguardBypassController.getBypassEnabled()) {
-            artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
-            if (artworkBitmap == null) {
-                artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
-            }
-        }
-
-        // Process artwork on a background thread and send the resulting bitmap to
-        // finishUpdateMediaMetaData.
-        if (metaDataChanged) {
-            for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
-                task.cancel(true);
-            }
-            mProcessArtworkTasks.clear();
-        }
-        if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {
-            mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
-                    allowEnterAnimation).execute(artworkBitmap));
-        } else {
-            finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
+        mColorExtractor.setHasMediaArtwork(false);
+        if (mScrimController != null) {
+            mScrimController.setHasBackdrop(false);
         }
 
         Trace.endSection();
     }
 
-    private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
-            @Nullable Bitmap bmp) {
-        Drawable artworkDrawable = null;
-        if (bmp != null) {
-            artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
-        }
-        boolean hasMediaArtwork = artworkDrawable != null;
-        boolean allowWhenShade = false;
-        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
-            Bitmap lockWallpaper =
-                    mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
-            if (lockWallpaper != null) {
-                artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
-                        mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
-                // We're in the SHADE mode on the SIM screen - yet we still need to show
-                // the lockscreen wallpaper in that mode.
-                allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
-            }
-        }
-
-        NotificationShadeWindowController windowController =
-                mNotificationShadeWindowController.get();
-        boolean hideBecauseOccluded = mKeyguardStateController.isOccluded();
-
-        final boolean hasArtwork = artworkDrawable != null;
-        mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
-        if (mScrimController != null) {
-            mScrimController.setHasBackdrop(hasArtwork);
-        }
-
-        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
-                && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
-                &&  mBiometricUnlockController != null && mBiometricUnlockController.getMode()
-                        != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                && !hideBecauseOccluded) {
-            // time to show some art!
-            if (mBackdrop.getVisibility() != View.VISIBLE) {
-                mBackdrop.setVisibility(View.VISIBLE);
-                if (allowEnterAnimation) {
-                    mBackdrop.setAlpha(0);
-                    mBackdrop.animate().alpha(1f);
-                } else {
-                    mBackdrop.animate().cancel();
-                    mBackdrop.setAlpha(1f);
-                }
-                if (windowController != null) {
-                    windowController.setBackdropShowing(true);
-                }
-                metaDataChanged = true;
-                if (DEBUG_MEDIA) {
-                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
-                }
-            }
-            if (metaDataChanged) {
-                if (mBackdropBack.getDrawable() != null) {
-                    Drawable drawable =
-                            mBackdropBack.getDrawable().getConstantState()
-                                    .newDrawable(mBackdropFront.getResources()).mutate();
-                    mBackdropFront.setImageDrawable(drawable);
-                    mBackdropFront.setAlpha(1f);
-                    mBackdropFront.setVisibility(View.VISIBLE);
-                } else {
-                    mBackdropFront.setVisibility(View.INVISIBLE);
-                }
-
-                if (DEBUG_MEDIA_FAKE_ARTWORK) {
-                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
-                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
-                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
-                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
-                } else {
-                    if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
-                        mWallapperDrawable =
-                                (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
-                    }
-                    mBackdropBack.setImageDrawable(artworkDrawable);
-                }
-
-                if (mBackdropFront.getVisibility() == View.VISIBLE) {
-                    if (DEBUG_MEDIA) {
-                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
-                                + mBackdropFront.getDrawable()
-                                + " to "
-                                + mBackdropBack.getDrawable());
-                    }
-                    mBackdropFront.animate()
-                            .setDuration(250)
-                            .alpha(0f).withEndAction(mHideBackdropFront);
-                }
-            }
-        } else {
-            // need to hide the album art, either because we are unlocked, on AOD
-            // or because the metadata isn't there to support it
-            if (mBackdrop.getVisibility() != View.GONE) {
-                if (DEBUG_MEDIA) {
-                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
-                }
-                boolean cannotAnimateDoze = mStatusBarStateController.isDozing()
-                        && !ScrimState.AOD.getAnimateChange();
-                if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode()
-                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                                || cannotAnimateDoze))
-                        || hideBecauseOccluded) {
-                    // We are unlocking directly - no animation!
-                    mBackdrop.setVisibility(View.GONE);
-                    mBackdropBack.setImageDrawable(null);
-                    if (windowController != null) {
-                        windowController.setBackdropShowing(false);
-                    }
-                } else {
-                    if (windowController != null) {
-                        windowController.setBackdropShowing(false);
-                    }
-                    mBackdrop.animate()
-                            .alpha(0)
-                            .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
-                            .setDuration(300)
-                            .setStartDelay(0)
-                            .withEndAction(() -> {
-                                mBackdrop.setVisibility(View.GONE);
-                                mBackdropFront.animate().cancel();
-                                mBackdropBack.setImageDrawable(null);
-                                mMainExecutor.execute(mHideBackdropFront);
-                            });
-                    if (mKeyguardStateController.isKeyguardFadingAway()) {
-                        mBackdrop.animate()
-                                .setDuration(
-                                        mKeyguardStateController.getShortenedFadingAwayDuration())
-                                .setStartDelay(
-                                        mKeyguardStateController.getKeyguardFadingAwayDelay())
-                                .setInterpolator(Interpolators.LINEAR)
-                                .start();
-                    }
-                }
-            }
-        }
-    }
-
     public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
             ScrimController scrimController, LockscreenWallpaper lockscreenWallpaper) {
         mBackdrop = backdrop;
@@ -758,15 +552,6 @@
         }
     };
 
-    private Bitmap processArtwork(Bitmap artwork) {
-        return mMediaArtworkProcessor.processArtwork(mContext, artwork);
-    }
-
-    @MainThread
-    private void removeTask(AsyncTask<?, ?, ?> task) {
-        mProcessArtworkTasks.remove(task);
-    }
-
     // TODO(b/273443374): remove
     public boolean isLockscreenWallpaperOnNotificationShade() {
         return mBackdrop != null && mLockscreenWallpaper != null
@@ -780,52 +565,6 @@
         return mBackdrop;
     }
 
-    /**
-     * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
-     */
-    private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> {
-
-        private final WeakReference<NotificationMediaManager> mManagerRef;
-        private final boolean mMetaDataChanged;
-        private final boolean mAllowEnterAnimation;
-
-        ProcessArtworkTask(NotificationMediaManager manager, boolean changed,
-                boolean allowAnimation) {
-            mManagerRef = new WeakReference<>(manager);
-            mMetaDataChanged = changed;
-            mAllowEnterAnimation = allowAnimation;
-        }
-
-        @Override
-        protected Bitmap doInBackground(Bitmap... bitmaps) {
-            NotificationMediaManager manager = mManagerRef.get();
-            if (manager == null || bitmaps.length == 0 || isCancelled()) {
-                return null;
-            }
-            return manager.processArtwork(bitmaps[0]);
-        }
-
-        @Override
-        protected void onPostExecute(@Nullable Bitmap result) {
-            NotificationMediaManager manager = mManagerRef.get();
-            if (manager != null && !isCancelled()) {
-                manager.removeTask(this);
-                manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result);
-            }
-        }
-
-        @Override
-        protected void onCancelled(Bitmap result) {
-            if (result != null) {
-                result.recycle();
-            }
-            NotificationMediaManager manager = mManagerRef.get();
-            if (manager != null) {
-                manager.removeTask(this);
-            }
-        }
-    }
-
     public interface MediaListener {
         /**
          * Called whenever there's new metadata or playback state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 63282d2..17da015 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -71,7 +71,8 @@
      * @param entry the entry for which a remote input is now active.
      * @param token a token identifying the view that is managing the remote input
      */
-    public void addRemoteInput(NotificationEntry entry, Object token) {
+    public void addRemoteInput(NotificationEntry entry, Object token,
+            @CompileTimeConstant String reason) {
         Objects.requireNonNull(entry);
         Objects.requireNonNull(token);
         boolean isActive = isRemoteInputActive(entry);
@@ -79,7 +80,9 @@
                 entry /* contains */, null /* remove */, token /* removeToken */);
         mLogger.logAddRemoteInput(entry.getKey()/* entryKey */,
                 isActive /* isRemoteInputAlreadyActive */,
-                found /* isRemoteInputFound */);
+                found /* isRemoteInputFound */,
+                reason /* reason */,
+                entry.getNotificationStyle()/* notificationStyle */);
         if (!found) {
             mOpen.add(new Pair<>(new WeakReference<>(entry), token));
         }
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 7f5829d..125c8efe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -31,7 +31,6 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpHandler;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
@@ -45,12 +44,10 @@
 import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
 import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.MediaArtworkProcessor;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -61,7 +58,6 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -71,7 +67,6 @@
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import dagger.Binds;
 import dagger.Lazy;
@@ -122,13 +117,9 @@
     @Provides
     static NotificationMediaManager provideNotificationMediaManager(
             Context context,
-            Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             NotificationVisibilityProvider visibilityProvider,
-            MediaArtworkProcessor mediaArtworkProcessor,
-            KeyguardBypassController keyguardBypassController,
             NotifPipeline notifPipeline,
             NotifCollection notifCollection,
-            @Main DelayableExecutor mainExecutor,
             MediaDataManager mediaDataManager,
             StatusBarStateController statusBarStateController,
             SysuiColorExtractor colorExtractor,
@@ -138,13 +129,9 @@
             DisplayManager displayManager) {
         return new NotificationMediaManager(
                 context,
-                notificationShadeWindowController,
                 visibilityProvider,
-                mediaArtworkProcessor,
-                keyguardBypassController,
                 notifPipeline,
                 notifCollection,
-                mainExecutor,
                 mediaDataManager,
                 statusBarStateController,
                 colorExtractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
deleted file mode 100644
index 732c115..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ /dev/null
@@ -1,131 +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.statusbar.notification;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-
-import androidx.palette.graphics.Palette;
-
-import java.util.List;
-
-/**
- * A gutted class that now contains only a color extraction utility used by the
- * MediaArtworkProcessor, which has otherwise supplanted this.
- *
- * TODO(b/182926117): move this into MediaArtworkProcessor.kt
- */
-public class MediaNotificationProcessor {
-
-    /**
-     * The population fraction to select a white or black color as the background over a color.
-     */
-    private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f;
-    private static final float BLACK_MAX_LIGHTNESS = 0.08f;
-    private static final float WHITE_MIN_LIGHTNESS = 0.90f;
-    private static final int RESIZE_BITMAP_AREA = 150 * 150;
-
-    private MediaNotificationProcessor() {
-    }
-
-    /**
-     * Finds an appropriate background swatch from media artwork.
-     *
-     * @param artwork Media artwork
-     * @return Swatch that should be used as the background of the media notification.
-     */
-    public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) {
-        return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate());
-    }
-
-    /**
-     * Finds an appropriate background swatch from the palette of media artwork.
-     *
-     * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
-     * @return Swatch that should be used as the background of the media notification.
-     */
-    public static Palette.Swatch findBackgroundSwatch(Palette palette) {
-        // by default we use the dominant palette
-        Palette.Swatch dominantSwatch = palette.getDominantSwatch();
-        if (dominantSwatch == null) {
-            return new Palette.Swatch(Color.WHITE, 100);
-        }
-
-        if (!isWhiteOrBlack(dominantSwatch.getHsl())) {
-            return dominantSwatch;
-        }
-        // Oh well, we selected black or white. Lets look at the second color!
-        List<Palette.Swatch> swatches = palette.getSwatches();
-        float highestNonWhitePopulation = -1;
-        Palette.Swatch second = null;
-        for (Palette.Swatch swatch : swatches) {
-            if (swatch != dominantSwatch
-                    && swatch.getPopulation() > highestNonWhitePopulation
-                    && !isWhiteOrBlack(swatch.getHsl())) {
-                second = swatch;
-                highestNonWhitePopulation = swatch.getPopulation();
-            }
-        }
-        if (second == null) {
-            return dominantSwatch;
-        }
-        if (dominantSwatch.getPopulation() / highestNonWhitePopulation
-                > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) {
-            // The dominant swatch is very dominant, lets take it!
-            // We're not filtering on white or black
-            return dominantSwatch;
-        } else {
-            return second;
-        }
-    }
-
-    /**
-     * Generate a palette builder for media artwork.
-     *
-     * For producing a smooth background transition, the palette is extracted from only the left
-     * side of the artwork.
-     *
-     * @param artwork Media artwork
-     * @return Builder that generates the {@link Palette} for the media artwork.
-     */
-    public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
-        // for the background we only take the left side of the image to ensure
-        // a smooth transition
-        return Palette.from(artwork)
-                .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight())
-                .clearFilters() // we want all colors, red / white / black ones too!
-                .resizeBitmapArea(RESIZE_BITMAP_AREA);
-    }
-
-    private static boolean isWhiteOrBlack(float[] hsl) {
-        return isBlack(hsl) || isWhite(hsl);
-    }
-
-    /**
-     * @return true if the color represents a color which is close to black.
-     */
-    private static boolean isBlack(float[] hslColor) {
-        return hslColor[2] <= BLACK_MAX_LIGHTNESS;
-    }
-
-    /**
-     * @return true if the color represents a color which is close to white.
-     */
-    private static boolean isWhite(float[] hslColor) {
-        return hslColor[2] >= WHITE_MIN_LIGHTNESS;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
index 9777031..ff89c62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
@@ -32,17 +32,24 @@
     fun logAddRemoteInput(
         entryKey: String,
         isRemoteInputAlreadyActive: Boolean,
-        isRemoteInputFound: Boolean
+        isRemoteInputFound: Boolean,
+        reason: String,
+        notificationStyle: String
     ) =
         logBuffer.log(
             TAG,
             DEBUG,
             {
                 str1 = entryKey
+                str2 = reason
+                str3 = notificationStyle
                 bool1 = isRemoteInputAlreadyActive
                 bool2 = isRemoteInputFound
             },
-            { "addRemoteInput entry: $str1, isAlreadyActive: $bool1, isFound:$bool2" }
+            {
+                "addRemoteInput reason:$str2 entry: $str1, style:$str3" +
+                    ", isAlreadyActive: $bool1, isFound:$bool2"
+            }
         )
 
     /** logs removeRemoteInput invocation of [RemoteInputController] */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2809cad..8129b83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -17,8 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.UNKNOWN_LAST_WAKE_TIME;
 
 import android.annotation.IntDef;
 import android.content.res.Resources;
@@ -30,7 +28,6 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.Trace;
-import android.view.HapticFeedbackConstants;
 
 import androidx.annotation.Nullable;
 
@@ -47,16 +44,17 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.logging.BiometricUnlockLogger;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -73,12 +71,14 @@
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
+
 /**
  * Controller which coordinates all the biometric unlocking actions with the UI.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 public class BiometricUnlockController extends KeyguardUpdateMonitorCallback implements Dumpable {
-    private static final long RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS = 400L;
     private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
     private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
     private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
@@ -175,6 +175,7 @@
     private final BiometricUnlockLogger mLogger;
     private final SystemClock mSystemClock;
     private final boolean mOrderUnlockAndWake;
+    private final DeviceEntryHapticsInteractor mHapticsInteractor;
 
     private long mLastFpFailureUptimeMillis;
     private int mNumConsecutiveFpFailures;
@@ -284,7 +285,8 @@
             ScreenOffAnimationController screenOffAnimationController,
             VibratorHelper vibrator,
             SystemClock systemClock,
-            FeatureFlags featureFlags
+            FeatureFlags featureFlags,
+            DeviceEntryHapticsInteractor hapticsInteractor
     ) {
         mPowerManager = powerManager;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -314,6 +316,7 @@
         mFeatureFlags = featureFlags;
         mOrderUnlockAndWake = resources.getBoolean(
                 com.android.internal.R.bool.config_orderUnlockAndWake);
+        mHapticsInteractor = hapticsInteractor;
 
         dumpManager.registerDumpable(this);
     }
@@ -434,7 +437,7 @@
         if (mode == MODE_WAKE_AND_UNLOCK
                 || mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING
                 || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) {
-            vibrateSuccess(biometricSourceType);
+            mHapticsInteractor.vibrateSuccess();
             onBiometricUnlockedWithKeyguardDismissal(biometricSourceType);
         }
         startWakeAndUnlock(mode);
@@ -498,8 +501,7 @@
             case MODE_WAKE_AND_UNLOCK:
                 if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
-                    mMediaManager.updateMediaMetaData(false /* metaDataChanged */,
-                            true /* allowEnterAnimation */);
+                    mMediaManager.updateMediaMetaData(false /* metaDataChanged */);
                 } else if (mMode == MODE_WAKE_AND_UNLOCK){
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK");
                 } else {
@@ -723,7 +725,7 @@
                 && !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                 KeyguardUpdateMonitor.getCurrentUser()))
                 || (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
-            vibrateError(biometricSourceType);
+            mHapticsInteractor.vibrateError();
         }
 
         cleanup();
@@ -750,45 +752,6 @@
         cleanup();
     }
 
-    // these haptics are for device-entry only
-    private void vibrateSuccess(BiometricSourceType type) {
-        if (mAuthController.isSfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
-                && lastWakeupFromPowerButtonWithinHapticThreshold()) {
-            mLogger.d("Skip auth success haptic. Power button was recently pressed.");
-            return;
-        }
-        if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            mVibratorHelper.performHapticFeedback(
-                    mKeyguardViewController.getViewRootImpl().getView(),
-                    HapticFeedbackConstants.CONFIRM
-            );
-        } else {
-            mVibratorHelper.vibrateAuthSuccess(
-                    getClass().getSimpleName() + ", type =" + type + "device-entry::success");
-        }
-    }
-
-    private boolean lastWakeupFromPowerButtonWithinHapticThreshold() {
-        final boolean lastWakeupFromPowerButton = mWakefulnessLifecycle.getLastWakeReason()
-                == PowerManager.WAKE_REASON_POWER_BUTTON;
-        return lastWakeupFromPowerButton
-                && mWakefulnessLifecycle.getLastWakeTime() != UNKNOWN_LAST_WAKE_TIME
-                && mSystemClock.uptimeMillis() - mWakefulnessLifecycle.getLastWakeTime()
-                < RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS;
-    }
-
-    private void vibrateError(BiometricSourceType type) {
-        if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            mVibratorHelper.performHapticFeedback(
-                    mKeyguardViewController.getViewRootImpl().getView(),
-                    HapticFeedbackConstants.REJECT
-            );
-        } else {
-            mVibratorHelper.vibrateAuthError(
-                    getClass().getSimpleName() + ", type =" + type + "device-entry::error");
-        }
-    }
-
     private void cleanup() {
         releaseBiometricWakeLock();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 92c786f..00fd9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -263,8 +263,7 @@
                 if (result.success) {
                     mCached = true;
                     mCache = result.bitmap;
-                    mMediaManager.updateMediaMetaData(
-                            true /* metaDataChanged */, true /* allowEnterAnimation */);
+                    mMediaManager.updateMediaMetaData(true /* metaDataChanged */);
                 }
                 mLoader = null;
             }
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 3adf338..400ac7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -961,7 +961,7 @@
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
         }
         if (isShowing) {
-            mMediaManager.updateMediaMetaData(false, animate && !isOccluded);
+            mMediaManager.updateMediaMetaData(false);
         }
         mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 2d14f6b..57a8e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -221,7 +221,7 @@
 
     @Override
     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
-        mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation);
+        mMediaManager.updateMediaMetaData(metaDataChanged);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
index e9e52a2..1670dd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
@@ -104,7 +104,7 @@
                 val callback =
                     object : WifiPickerTracker.WifiPickerTrackerCallback {
                         override fun onWifiEntriesChanged() {
-                            val connectedEntry = wifiPickerTracker?.connectedWifiEntry
+                            val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection
                             logOnWifiEntriesChanged(connectedEntry)
 
                             val secondaryNetworks =
@@ -217,6 +217,21 @@
             .stateIn(scope, SharingStarted.Eagerly, emptyList())
 
     /**
+     * [WifiPickerTracker.getConnectedWifiEntry] stores a [MergedCarrierEntry] separately from the
+     * [WifiEntry] for the primary connection. Therefore, we have to prefer the carrier-merged entry
+     * if it exists, falling back on the connected entry if null
+     */
+    private val WifiPickerTracker?.mergedOrPrimaryConnection: WifiEntry?
+        get() {
+            val mergedEntry: MergedCarrierEntry? = this?.mergedCarrierEntry
+            return if (mergedEntry != null && mergedEntry.isDefaultNetwork) {
+                mergedEntry
+            } else {
+                this?.connectedWifiEntry
+            }
+        }
+
+    /**
      * Converts WifiTrackerLib's [WifiEntry] into our internal model only if the entry is the
      * primary network. Returns an inactive network if it's not primary.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 7c96029..ceed81a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -657,7 +657,7 @@
         mEditText.setText(mEntry.remoteInputText);
         mEditText.setSelection(mEditText.length());
         mEditText.requestFocus();
-        mController.addRemoteInput(mEntry, mToken);
+        mController.addRemoteInput(mEntry, mToken, "RemoteInputView#focus");
         setAttachment(mEntry.remoteInputAttachment);
 
         updateSendButton();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 3df9cbb..91409a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -11,11 +11,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
+import com.android.systemui.communal.shared.CommunalContentSize
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
@@ -59,9 +62,12 @@
 
     @Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
 
+    private lateinit var communalRepository: FakeCommunalRepository
+
     private lateinit var logBuffer: LogBuffer
 
     private val testDispatcher = StandardTestDispatcher()
+
     private val testScope = TestScope(testDispatcher)
 
     @Before
@@ -71,6 +77,14 @@
         logBuffer = FakeLogBuffer.Factory.create()
 
         featureFlagEnabled(true)
+        communalRepository = FakeCommunalRepository()
+        communalRepository.setIsCommunalEnabled(true)
+
+        overrideResource(
+            R.array.config_communalWidgetAllowlist,
+            arrayOf(componentName1, componentName2)
+        )
+
         whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
         whenever(userTracker.userHandle).thenReturn(userHandle)
     }
@@ -219,11 +233,36 @@
             Mockito.verify(appWidgetHost).stopListening()
         }
 
+    @Test
+    fun getCommunalWidgetAllowList_onInit() {
+        testScope.runTest {
+            val repository = initCommunalWidgetRepository()
+            val communalWidgetAllowlist = repository.communalWidgetAllowlist
+            assertThat(
+                    listOf(
+                        CommunalWidgetMetadata(
+                            componentName = componentName1,
+                            priority = 2,
+                            sizes = listOf(CommunalContentSize.HALF)
+                        ),
+                        CommunalWidgetMetadata(
+                            componentName = componentName2,
+                            priority = 1,
+                            sizes = listOf(CommunalContentSize.HALF)
+                        )
+                    )
+                )
+                .containsExactly(*communalWidgetAllowlist.toTypedArray())
+        }
+    }
+
     private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
         return CommunalWidgetRepositoryImpl(
+            context,
             appWidgetManager,
             appWidgetHost,
             broadcastDispatcher,
+            communalRepository,
             packageManager,
             userManager,
             userTracker,
@@ -282,4 +321,9 @@
     private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
         whenever(appWidgetManager.installedProviders).thenReturn(providers)
     }
+
+    companion object {
+        const val componentName1 = "component name 1"
+        const val componentName2 = "component name 2"
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
new file mode 100644
index 0000000..9b8e581
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.deviceentry.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.logging.BiometricUnlockLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.mock
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
+
+    private lateinit var repository: DeviceEntryHapticsRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var keyEventRepository: FakeKeyEventRepository
+    private lateinit var powerRepository: FakePowerRepository
+    private lateinit var systemClock: FakeSystemClock
+    private lateinit var underTest: DeviceEntryHapticsInteractor
+
+    @Before
+    fun setUp() {
+        repository = DeviceEntryHapticsRepositoryImpl()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        keyEventRepository = FakeKeyEventRepository()
+        powerRepository = FakePowerRepository()
+        systemClock = FakeSystemClock()
+        underTest =
+            DeviceEntryHapticsInteractor(
+                repository = repository,
+                fingerprintPropertyRepository = fingerprintPropertyRepository,
+                biometricSettingsRepository = biometricSettingsRepository,
+                keyEventInteractor = KeyEventInteractor(keyEventRepository),
+                powerInteractor =
+                    PowerInteractor(
+                        powerRepository,
+                        mock(FalsingCollector::class.java),
+                        mock(ScreenOffAnimationController::class.java),
+                        mock(StatusBarStateController::class.java),
+                    ),
+                systemClock = systemClock,
+                logger = mock(BiometricUnlockLogger::class.java),
+            )
+    }
+
+    @Test
+    fun nonPowerButtonFPS_vibrateSuccess() = runTest {
+        val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+        setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+        underTest.vibrateSuccess()
+        assertThat(playSuccessHaptic).isTrue()
+    }
+
+    @Test
+    fun powerButtonFPS_vibrateSuccess() = runTest {
+        val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+        setPowerButtonFingerprintProperty()
+        setFingerprintEnrolled()
+        keyEventRepository.setPowerButtonDown(false)
+
+        // It's been 10 seconds since the last power button wakeup
+        setAwakeFromPowerButton()
+        runCurrent()
+        systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000)
+
+        underTest.vibrateSuccess()
+        assertThat(playSuccessHaptic).isTrue()
+    }
+
+    @Test
+    fun powerButtonFPS_powerDown_doNotVibrateSuccess() = runTest {
+        val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+        setPowerButtonFingerprintProperty()
+        setFingerprintEnrolled()
+        keyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
+
+        // It's been 10 seconds since the last power button wakeup
+        setAwakeFromPowerButton()
+        runCurrent()
+        systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000)
+
+        underTest.vibrateSuccess()
+        assertThat(playSuccessHaptic).isFalse()
+    }
+
+    @Test
+    fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = runTest {
+        val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+        setPowerButtonFingerprintProperty()
+        setFingerprintEnrolled()
+        keyEventRepository.setPowerButtonDown(false)
+
+        // It's only been 50ms since the last power button wakeup
+        setAwakeFromPowerButton()
+        runCurrent()
+        systemClock.setUptimeMillis(systemClock.uptimeMillis() + 50)
+
+        underTest.vibrateSuccess()
+        assertThat(playSuccessHaptic).isFalse()
+    }
+
+    @Test
+    fun nonPowerButtonFPS_vibrateError() = runTest {
+        val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+        setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+        underTest.vibrateError()
+        assertThat(playErrorHaptic).isTrue()
+    }
+
+    @Test
+    fun powerButtonFPS_vibrateError() = runTest {
+        val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+        setPowerButtonFingerprintProperty()
+        setFingerprintEnrolled()
+        underTest.vibrateError()
+        assertThat(playErrorHaptic).isTrue()
+    }
+
+    @Test
+    fun powerButtonFPS_powerDown_doNotVibrateError() = runTest {
+        val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+        setPowerButtonFingerprintProperty()
+        setFingerprintEnrolled()
+        keyEventRepository.setPowerButtonDown(true)
+        underTest.vibrateError()
+        assertThat(playErrorHaptic).isFalse()
+    }
+
+    private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) {
+        fingerprintPropertyRepository.setProperties(
+            sensorId = 0,
+            strength = SensorStrength.STRONG,
+            sensorType = fingerprintSensorType,
+            sensorLocations = mapOf(),
+        )
+    }
+
+    private fun setPowerButtonFingerprintProperty() {
+        setFingerprintSensorType(FingerprintSensorType.POWER_BUTTON)
+    }
+
+    private fun setFingerprintEnrolled() {
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+    }
+
+    private fun setAwakeFromPowerButton() {
+        powerRepository.updateWakefulness(
+            WakefulnessState.AWAKE,
+            WakeSleepReason.POWER_BUTTON,
+            WakeSleepReason.POWER_BUTTON,
+            powerButtonLaunchGestureTriggered = false,
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 0ee348e..7750d25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -28,6 +28,8 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
+import kotlin.math.max
+import kotlin.test.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -149,26 +151,52 @@
     }
 
     @Test
-    fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() {
-        // GIVEN max velocity and slider progress
-        val progress = 1f
-        val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
-        val ticks = VibrationEffect.startComposition()
-        repeat(config.numberOfLowTicks) {
-            ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
-        }
+    fun playHapticAtProgress_beforeNextDragThreshold_playsLowTicksOnce() {
+        // GIVEN max velocity and a slider progress at half progress
+        val firstProgress = 0.5f
+        val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress)
+
+        // Given a second slider progress event smaller than the progress threshold
+        val secondProgress = firstProgress + max(0f, config.deltaProgressForDragThreshold - 0.01f)
 
         // GIVEN system running for 1s
         clock.advanceTime(1000)
 
-        // WHEN two calls to play occur with the required threshold separation
-        sliderHapticFeedbackProvider.onProgress(progress)
+        // WHEN two calls to play occur with the required threshold separation (time and progress)
+        sliderHapticFeedbackProvider.onProgress(firstProgress)
         clock.advanceTime(dragTextureThresholdMillis.toLong())
-        sliderHapticFeedbackProvider.onProgress(progress)
+        sliderHapticFeedbackProvider.onProgress(secondProgress)
 
-        // THEN the correct composition plays two times
-        verify(vibratorHelper, times(2))
-            .vibrate(eq(ticks.compose()), any(VibrationAttributes::class.java))
+        // THEN Only the first compositions plays
+        verify(vibratorHelper, times(1))
+            .vibrate(eq(firstTicks), any(VibrationAttributes::class.java))
+        verify(vibratorHelper, times(1))
+            .vibrate(any(VibrationEffect::class.java), any(VibrationAttributes::class.java))
+    }
+
+    @Test
+    fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() {
+        // GIVEN max velocity and a slider progress at half progress
+        val firstProgress = 0.5f
+        val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress)
+
+        // Given a second slider progress event beyond progress threshold
+        val secondProgress = firstProgress + config.deltaProgressForDragThreshold + 0.01f
+        val secondTicks = generateTicksComposition(config.maxVelocityToScale, secondProgress)
+
+        // GIVEN system running for 1s
+        clock.advanceTime(1000)
+
+        // WHEN two calls to play occur with the required threshold separation (time and progress)
+        sliderHapticFeedbackProvider.onProgress(firstProgress)
+        clock.advanceTime(dragTextureThresholdMillis.toLong())
+        sliderHapticFeedbackProvider.onProgress(secondProgress)
+
+        // THEN the correct compositions play
+        verify(vibratorHelper, times(1))
+            .vibrate(eq(firstTicks), any(VibrationAttributes::class.java))
+        verify(vibratorHelper, times(1))
+            .vibrate(eq(secondTicks), any(VibrationAttributes::class.java))
     }
 
     @Test
@@ -229,6 +257,38 @@
             .vibrate(eq(bookendVibration), any(VibrationAttributes::class.java))
     }
 
+    fun dragTextureLastProgress_afterDragTextureHaptics_keepsLastDragTextureProgress() {
+        // GIVEN max velocity and a slider progress at half progress
+        val progress = 0.5f
+
+        // GIVEN system running for 1s
+        clock.advanceTime(1000)
+
+        // WHEN a drag texture plays
+        sliderHapticFeedbackProvider.onProgress(progress)
+
+        // THEN the dragTextureLastProgress remembers the latest progress
+        assertEquals(progress, sliderHapticFeedbackProvider.dragTextureLastProgress)
+    }
+
+    @Test
+    fun dragTextureLastProgress_afterDragTextureHaptics_resetsOnHandleReleased() {
+        // GIVEN max velocity and a slider progress at half progress
+        val progress = 0.5f
+
+        // GIVEN system running for 1s
+        clock.advanceTime(1000)
+
+        // WHEN a drag texture plays
+        sliderHapticFeedbackProvider.onProgress(progress)
+
+        // WHEN the handle is released
+        sliderHapticFeedbackProvider.onHandleReleasedFromTouch()
+
+        // THEN the dragTextureLastProgress tracker is reset
+        assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress)
+    }
+
     private fun scaleAtBookends(velocity: Float): Float {
         val range = config.upperBookendScale - config.lowerBookendScale
         val interpolatedVelocity =
@@ -244,4 +304,15 @@
         val bump = interpolatedVelocity * config.additionalVelocityMaxBump
         return interpolatedProgress * range + config.progressBasedDragMinScale + bump
     }
+
+    private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect {
+        val ticks = VibrationEffect.startComposition()
+        repeat(config.numberOfLowTicks) {
+            ticks.addPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+                scaleAtProgressChange(velocity, progress)
+            )
+        }
+        return ticks.compose()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index d8cdf29..90fd652 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -147,7 +147,7 @@
             emptyMap()
         )
         verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
-        verify(authController, atLeastOnce()).addCallback(authControllerCallback.capture())
+        verify(authController, times(2)).addCallback(authControllerCallback.capture())
     }
 
     @Test
@@ -314,18 +314,18 @@
     fun faceEnrollmentChangeIsPropagatedForTheCurrentUser() =
         testScope.runTest {
             createBiometricSettingsRepository()
+            val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
             faceAuthIsEnabledByBiometricManager()
 
             doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
 
             runCurrent()
-            clearInvocations(authController)
 
-            whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false)
-            val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+            enrollmentChange(FACE, PRIMARY_USER_ID, false)
 
             assertThat(faceAuthAllowed()).isFalse()
-            verify(authController).addCallback(authControllerCallback.capture())
+
             enrollmentChange(REAR_FINGERPRINT, PRIMARY_USER_ID, true)
 
             assertThat(faceAuthAllowed()).isFalse()
@@ -375,25 +375,20 @@
     fun faceEnrollmentChangesArePropagatedAfterUserSwitch() =
         testScope.runTest {
             createBiometricSettingsRepository()
+            val faceAuthAllowed by collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
             verify(biometricManager)
                 .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
 
             userRepository.setSelectedUserInfo(ANOTHER_USER)
             doNotDisableKeyguardAuthFeatures(ANOTHER_USER_ID)
             biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
-
-            runCurrent()
-            clearInvocations(authController)
-
-            val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
-            runCurrent()
-
-            verify(authController).addCallback(authControllerCallback.capture())
-
+            onNonStrongAuthChanged(true, ANOTHER_USER_ID)
             whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
             enrollmentChange(FACE, ANOTHER_USER_ID, true)
+            runCurrent()
 
-            assertThat(faceAuthAllowed()).isTrue()
+            assertThat(faceAuthAllowed).isTrue()
         }
 
     @Test
@@ -637,6 +632,7 @@
             val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
 
             faceAuthIsEnrolled()
+            enrollmentChange(FACE, PRIMARY_USER_ID, true)
             deviceIsInPostureThatSupportsFaceAuth()
             doNotDisableKeyguardAuthFeatures()
             faceAuthIsStrongBiometric()
@@ -660,6 +656,7 @@
             val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
 
             faceAuthIsEnrolled()
+            enrollmentChange(FACE, PRIMARY_USER_ID, true)
             deviceIsInPostureThatSupportsFaceAuth()
             doNotDisableKeyguardAuthFeatures()
             faceAuthIsNonStrongBiometric()
@@ -753,7 +750,9 @@
         }
 
     private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
-        authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
+        authControllerCallback.allValues.forEach {
+            it.onEnrollmentsChanged(biometricType, userId, enabled)
+        }
     }
 
     private fun doNotDisableKeyguardAuthFeatures(userId: Int = PRIMARY_USER_ID) {
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 06eb0dd..6ad2d73 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
@@ -43,7 +43,7 @@
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.DismissCallbackRegistry
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -95,6 +95,7 @@
         FakeDeviceEntryFingerprintAuthRepository
     private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
     private lateinit var powerInteractor: PowerInteractor
+    private lateinit var fakeBiometricSettingsRepository: FakeBiometricSettingsRepository
 
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig
@@ -123,6 +124,8 @@
         facePropertyRepository = FakeFacePropertyRepository()
         fakeKeyguardRepository = FakeKeyguardRepository()
         powerInteractor = PowerInteractorFactory.create().powerInteractor
+        fakeBiometricSettingsRepository = FakeBiometricSettingsRepository()
+
         underTest =
             SystemUIKeyguardFaceAuthInteractor(
                 mContext,
@@ -147,7 +150,7 @@
                     mock(StatusBarStateController::class.java),
                     mock(KeyguardStateController::class.java),
                     bouncerRepository,
-                    mock(BiometricSettingsRepository::class.java),
+                    fakeBiometricSettingsRepository,
                     FakeSystemClock(),
                     keyguardUpdateMonitor,
                 ),
@@ -160,6 +163,7 @@
                 facePropertyRepository,
                 faceWakeUpTriggersConfig,
                 powerInteractor,
+                fakeBiometricSettingsRepository,
             )
     }
 
@@ -481,6 +485,7 @@
     fun faceUnlockIsDisabledWhenFpIsLockedOut() =
         testScope.runTest {
             underTest.start()
+            fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
 
             fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
             runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 34360d2..6cdf4ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -4,7 +4,9 @@
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
@@ -20,6 +22,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 
 @RunWith(AndroidTestingRunner::class)
@@ -37,10 +40,11 @@
 
     private val view: MediaProjectionAppSelectorView = mock()
     private val policyResolver: ScreenCaptureDevicePolicyResolver = mock()
+    private val logger = mock<MediaProjectionMetricsLogger>()
 
     private val thumbnailLoader = FakeThumbnailLoader()
 
-    private val controller =
+    private fun createController(isFirstStart: Boolean = true) =
         MediaProjectionAppSelectorController(
             taskListProvider,
             view,
@@ -50,6 +54,8 @@
             appSelectorComponentName,
             callerPackageName,
             thumbnailLoader,
+            isFirstStart,
+            logger
         )
 
     @Before
@@ -61,7 +67,7 @@
     fun initNoRecentTasks_bindsEmptyList() {
         taskListProvider.tasks = emptyList()
 
-        controller.init()
+        createController().init()
 
         verify(view).bind(emptyList())
     }
@@ -70,7 +76,7 @@
     fun initOneRecentTask_bindsList() {
         taskListProvider.tasks = listOf(createRecentTask(taskId = 1))
 
-        controller.init()
+        createController().init()
 
         verify(view).bind(listOf(createRecentTask(taskId = 1)))
     }
@@ -86,7 +92,7 @@
             )
         taskListProvider.tasks = tasks
 
-        controller.init()
+        createController().init()
 
         assertThat(thumbnailLoader.capturedTaskIds).containsExactly(2, 3)
     }
@@ -101,7 +107,7 @@
             )
         taskListProvider.tasks = tasks
 
-        controller.init()
+        createController().init()
 
         verify(view)
             .bind(
@@ -124,7 +130,7 @@
             )
         taskListProvider.tasks = tasks
 
-        controller.init()
+        createController().init()
 
         verify(view)
             .bind(
@@ -147,7 +153,7 @@
             )
         taskListProvider.tasks = tasks
 
-        controller.init()
+        createController().init()
 
         verify(view)
             .bind(
@@ -172,7 +178,7 @@
             )
         taskListProvider.tasks = tasks
 
-        controller.init()
+        createController().init()
 
         verify(view)
             .bind(
@@ -199,11 +205,29 @@
         taskListProvider.tasks = tasks
 
         givenCaptureAllowed(isAllow = false)
-        controller.init()
+        createController().init()
 
         verify(view).bind(emptyList())
     }
 
+    @Test
+    fun init_firstStart_logsAppSelectorDisplayed() {
+        val controller = createController(isFirstStart = true)
+
+        controller.init()
+
+        verify(logger).notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+    }
+
+    @Test
+    fun init_notFirstStart_doesNotLogAppSelectorDisplayed() {
+        val controller = createController(isFirstStart = false)
+
+        controller.init()
+
+        verify(logger, never()).notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+    }
+
     private fun givenCaptureAllowed(isAllow: Boolean) {
         whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow)
     }
@@ -216,6 +240,7 @@
     ): RecentTask {
         return RecentTask(
             taskId = taskId,
+            displayId = 0,
             topActivityComponent = topActivityComponent,
             baseIntentComponent = ComponentName("com", "Test"),
             userId = userId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index 2c7ee56..d75553f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -128,6 +128,7 @@
     private fun createRecentTask(taskId: Int): RecentTask =
         RecentTask(
             taskId = taskId,
+            displayId = 0,
             userId = 0,
             topActivityComponent = null,
             baseIntentComponent = null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 6e6833d..90d2e78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -17,8 +17,10 @@
 package com.android.systemui.screenrecord;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
@@ -37,6 +39,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
+import com.android.systemui.mediaprojection.SessionCreationSource;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
 import com.android.systemui.plugins.ActivityStarter;
@@ -76,6 +80,8 @@
     private ActivityStarter mActivityStarter;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
 
     private FakeFeatureFlags mFeatureFlags;
     private RecordingController mController;
@@ -86,8 +92,15 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mFeatureFlags = new FakeFeatureFlags();
-        mController = new RecordingController(mMainExecutor, mBroadcastDispatcher, mContext,
-                mFeatureFlags, mUserContextProvider, () -> mDevicePolicyResolver, mUserTracker);
+        mController = new RecordingController(
+                mMainExecutor,
+                mBroadcastDispatcher,
+                mContext,
+                mFeatureFlags,
+                mUserContextProvider,
+                () -> mDevicePolicyResolver,
+                mUserTracker,
+                mMediaProjectionMetricsLogger);
         mController.addCallback(mCallback);
     }
 
@@ -269,4 +282,21 @@
 
         assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
     }
+
+    @Test
+    public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+        when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
+
+        mController.createScreenRecordDialog(mContext, mFeatureFlags,
+                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+        verify(mMediaProjectionMetricsLogger)
+                .notifyProjectionInitiated(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
deleted file mode 100644
index e4da53a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar
-
-import com.google.common.truth.Truth.assertThat
-
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Point
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-private const val WIDTH = 200
-private const val HEIGHT = 200
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class MediaArtworkProcessorTest : SysuiTestCase() {
-
-    private var screenWidth = 0
-    private var screenHeight = 0
-
-    private lateinit var processor: MediaArtworkProcessor
-
-    @Before
-    fun setUp() {
-        processor = MediaArtworkProcessor()
-
-        val point = Point()
-        checkNotNull(context.display).getSize(point)
-        screenWidth = point.x
-        screenHeight = point.y
-    }
-
-    @After
-    fun tearDown() {
-        processor.clearCache()
-    }
-
-    @Test
-    fun testProcessArtwork() {
-        // GIVEN some "artwork", which is just a solid blue image
-        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
-        Canvas(artwork).drawColor(Color.BLUE)
-        // WHEN the background is created from the artwork
-        val background = processor.processArtwork(context, artwork)!!
-        // THEN the background has the size of the screen that has been downsamples
-        assertThat(background.height).isLessThan(screenHeight)
-        assertThat(background.width).isLessThan(screenWidth)
-        assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
-    }
-
-    @Test
-    fun testCache() {
-        // GIVEN a solid blue image
-        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
-        Canvas(artwork).drawColor(Color.BLUE)
-        // WHEN the background is processed twice
-        val background1 = processor.processArtwork(context, artwork)!!
-        val background2 = processor.processArtwork(context, artwork)!!
-        // THEN the two bitmaps are the same
-        // Note: This is currently broken and trying to use caching causes issues
-        assertThat(background1).isNotSameInstanceAs(background2)
-    }
-
-    @Test
-    fun testConfig() {
-        // GIVEN some which is not ARGB_8888
-        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ALPHA_8)
-        Canvas(artwork).drawColor(Color.BLUE)
-        // WHEN the background is created from the artwork
-        val background = processor.processArtwork(context, artwork)!!
-        // THEN the background has Config ARGB_8888
-        assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
-    }
-
-    @Test
-    fun testRecycledArtwork() {
-        // GIVEN some "artwork", which is just a solid blue image
-        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
-        Canvas(artwork).drawColor(Color.BLUE)
-        // AND the artwork is recycled
-        artwork.recycle()
-        // WHEN the background is created from the artwork
-        val background = processor.processArtwork(context, artwork)
-        // THEN the processed bitmap is null
-        assertThat(background).isNull()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
index 9d6ea85..cfcf425 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -48,9 +48,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        doCallRealMethod()
-            .whenever(notificationMediaManager)
-            .updateMediaMetaData(anyBoolean(), anyBoolean())
+        doCallRealMethod().whenever(notificationMediaManager).updateMediaMetaData(anyBoolean())
         doReturn(mockBackDropView).whenever(notificationMediaManager).backDropView
     }
 
@@ -62,7 +60,7 @@
         notificationMediaManager.mIsLockscreenLiveWallpaperEnabled = true
         for (metaDataChanged in listOf(true, false)) {
             for (allowEnterAnimation in listOf(true, false)) {
-                notificationMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
+                notificationMediaManager.updateMediaMetaData(metaDataChanged)
                 verify(notificationMediaManager, never()).mediaMetadata
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
deleted file mode 100644
index aeb5b03..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ /dev/null
@@ -1,87 +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.statusbar.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.palette.graphics.Palette;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MediaNotificationProcessorTest extends SysuiTestCase {
-
-    private static final int BITMAP_WIDTH = 10;
-    private static final int BITMAP_HEIGHT = 10;
-
-    /**
-     * Color tolerance is borrowed from the AndroidX test utilities for Palette.
-     */
-    private static final int COLOR_TOLERANCE = 8;
-
-    @Nullable private Bitmap mArtwork;
-
-    @After
-    public void tearDown() {
-        if (mArtwork != null) {
-            mArtwork.recycle();
-            mArtwork = null;
-        }
-    }
-
-    @Test
-    public void findBackgroundSwatch_white() {
-        // Given artwork that is completely white.
-        mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(mArtwork);
-        canvas.drawColor(Color.WHITE);
-        // WHEN the background swatch is computed
-        Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
-        // THEN the swatch color is white
-        assertCloseColors(swatch.getRgb(), Color.WHITE);
-    }
-
-    @Test
-    public void findBackgroundSwatch_red() {
-        // Given artwork that is completely red.
-        mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(mArtwork);
-        canvas.drawColor(Color.RED);
-        // WHEN the background swatch is computed
-        Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
-        // THEN the swatch color is red
-        assertCloseColors(swatch.getRgb(), Color.RED);
-    }
-
-    static void assertCloseColors(int expected, int actual) {
-        assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual));
-        assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
-        assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 700de53..8344cd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -18,7 +18,9 @@
 
 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -38,7 +40,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
-import android.view.HapticFeedbackConstants;
 import android.view.ViewRootImpl;
 
 import com.android.internal.logging.MetricsLogger;
@@ -47,6 +48,7 @@
 import com.android.keyguard.logging.BiometricUnlockLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -122,6 +124,8 @@
     private BiometricUnlockLogger mLogger;
     @Mock
     private ViewRootImpl mViewRootImpl;
+    @Mock
+    private DeviceEntryHapticsInteractor mDeviceEntryHapticsInteractor;
     private final FakeSystemClock mSystemClock = new FakeSystemClock();
     private FakeFeatureFlags mFeatureFlags;
     private BiometricUnlockController mBiometricUnlockController;
@@ -158,7 +162,8 @@
                 mAuthController, mStatusBarStateController,
                 mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
                 mSystemClock,
-                mFeatureFlags
+                mFeatureFlags,
+                mDeviceEntryHapticsInteractor
         );
         biometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
         biometricUnlockController.addListener(mBiometricUnlockEventsListener);
@@ -462,145 +467,23 @@
     }
 
     @Test
-    public void onSideFingerprintSuccess_recentPowerButtonPress_noHaptic() {
-        // GIVEN side fingerprint enrolled, last wake reason was power button
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-
-        // GIVEN last wake time just occurred
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
+    public void onFingerprintSuccess_requestSuccessHaptic() {
         // WHEN biometric fingerprint succeeds
         givenFingerprintModeUnlockCollapsing();
         mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
                 true);
 
-        // THEN DO NOT vibrate the device
-        verify(mVibratorHelper, never()).vibrateAuthSuccess(anyString());
+        // THEN always vibrate the device
+        verify(mDeviceEntryHapticsInteractor).vibrateSuccess();
     }
 
     @Test
-    public void onSideFingerprintSuccess_oldPowerButtonPress_playHaptic() {
-        // GIVEN side fingerprint enrolled, last wake reason was power button
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-
-        // GIVEN last wake time was 500ms ago
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-        mSystemClock.advanceTime(500);
-
-        // WHEN biometric fingerprint succeeds
-        givenFingerprintModeUnlockCollapsing();
-        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
-                true);
-
-        // THEN vibrate the device
-        verify(mVibratorHelper).vibrateAuthSuccess(anyString());
-    }
-
-    @Test
-    public void onSideFingerprintSuccess_oldPowerButtonPress_playOneWayHaptic() {
-        // GIVEN oneway haptics is enabled
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
-        // GIVEN side fingerprint enrolled, last wake reason was power button
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-
-        // GIVEN last wake time was 500ms ago
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-        mSystemClock.advanceTime(500);
-
-        // WHEN biometric fingerprint succeeds
-        givenFingerprintModeUnlockCollapsing();
-        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
-                true);
-
-        // THEN vibrate the device
-        verify(mVibratorHelper).performHapticFeedback(
-                any(),
-                eq(HapticFeedbackConstants.CONFIRM)
-        );
-    }
-
-    @Test
-    public void onSideFingerprintSuccess_recentGestureWakeUp_playHaptic() {
-        // GIVEN side fingerprint enrolled, wakeup just happened
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
-        // GIVEN last wake reason was from a gesture
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_GESTURE);
-
-        // WHEN biometric fingerprint succeeds
-        givenFingerprintModeUnlockCollapsing();
-        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
-                true);
-
-        // THEN vibrate the device
-        verify(mVibratorHelper).vibrateAuthSuccess(anyString());
-    }
-
-    @Test
-    public void onSideFingerprintSuccess_recentGestureWakeUp_playOnewayHaptic() {
-        //GIVEN oneway haptics is enabled
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
-        // GIVEN side fingerprint enrolled, wakeup just happened
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
-        // GIVEN last wake reason was from a gesture
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_GESTURE);
-
-        // WHEN biometric fingerprint succeeds
-        givenFingerprintModeUnlockCollapsing();
-        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
-                true);
-
-        // THEN vibrate the device
-        verify(mVibratorHelper).performHapticFeedback(
-                any(),
-                eq(HapticFeedbackConstants.CONFIRM)
-        );
-    }
-
-    @Test
-    public void onSideFingerprintFail_alwaysPlaysHaptic() {
-        // GIVEN side fingerprint enrolled, last wake reason was recent power button
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
+    public void onFingerprintFail_requestErrorHaptic() {
         // WHEN biometric fingerprint fails
         mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
 
         // THEN always vibrate the device
-        verify(mVibratorHelper).vibrateAuthError(anyString());
-    }
-
-    @Test
-    public void onSideFingerprintFail_alwaysPlaysOneWayHaptic() {
-        // GIVEN oneway haptics is enabled
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
-        // GIVEN side fingerprint enrolled, last wake reason was recent power button
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-        when(mWakefulnessLifecycle.getLastWakeReason())
-                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
-        // WHEN biometric fingerprint fails
-        mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
-
-        // THEN always vibrate the device
-        verify(mVibratorHelper).performHapticFeedback(
-                any(),
-                eq(HapticFeedbackConstants.REJECT)
-        );
+        verify(mDeviceEntryHapticsInteractor).vibrateError();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
index c2f5665..3126362 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
@@ -201,11 +201,11 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.isWifiDefault)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isDefaultNetwork).thenReturn(true)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
             getCallback().onWifiEntriesChanged()
 
             assertThat(latest).isTrue()
@@ -229,11 +229,11 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.isWifiDefault)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isDefaultNetwork).thenReturn(false)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
             getCallback().onWifiEntriesChanged()
 
             assertThat(latest).isFalse()
@@ -526,13 +526,14 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isPrimaryNetwork).thenReturn(true)
                     whenever(this.level).thenReturn(3)
                     whenever(this.subscriptionId).thenReturn(567)
+                    whenever(this.isDefaultNetwork).thenReturn(true)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
             getCallback().onWifiEntriesChanged()
 
             assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
@@ -546,11 +547,12 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isPrimaryNetwork).thenReturn(true)
+                    whenever(this.isDefaultNetwork).thenReturn(true)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
             whenever(wifiManager.maxSignalLevel).thenReturn(5)
 
             getCallback().onWifiEntriesChanged()
@@ -566,12 +568,13 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isPrimaryNetwork).thenReturn(true)
                     whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
+                    whenever(this.isDefaultNetwork).thenReturn(true)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
 
             getCallback().onWifiEntriesChanged()
 
@@ -628,11 +631,12 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isPrimaryNetwork).thenReturn(false)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
             getCallback().onWifiEntriesChanged()
 
             assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
@@ -717,12 +721,14 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
 
-            val wifiEntry =
+            val mergedEntry =
                 mock<MergedCarrierEntry>().apply {
                     whenever(this.isPrimaryNetwork).thenReturn(true)
                     whenever(this.level).thenReturn(3)
+                    whenever(this.isDefaultNetwork).thenReturn(true)
                 }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
             getCallback().onWifiEntriesChanged()
 
             assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
@@ -730,6 +736,7 @@
 
             // WHEN we lose our current network
             whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(null)
             getCallback().onWifiEntriesChanged()
 
             // THEN we update to no network
@@ -767,6 +774,56 @@
         }
 
     @Test
+    fun wifiNetwork_carrierMerged_default_usesCarrierMergedInfo() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wifiNetwork)
+
+            val mergedEntry =
+                mock<MergedCarrierEntry>().apply {
+                    whenever(this.isPrimaryNetwork).thenReturn(true)
+                    whenever(this.level).thenReturn(3)
+                    whenever(this.isDefaultNetwork).thenReturn(true)
+                }
+            val wifiEntry =
+                mock<WifiEntry>().apply {
+                    whenever(this.isPrimaryNetwork).thenReturn(true)
+                    whenever(this.level).thenReturn(1)
+                    whenever(this.title).thenReturn(TITLE)
+                }
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+
+            getCallback().onWifiEntriesChanged()
+
+            assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+        }
+
+    @Test
+    fun wifiNetwork_carrierMerged_notDefault_usesConnectedInfo() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wifiNetwork)
+
+            val mergedEntry =
+                mock<MergedCarrierEntry>().apply {
+                    whenever(this.isPrimaryNetwork).thenReturn(true)
+                    whenever(this.level).thenReturn(3)
+                    whenever(this.isDefaultNetwork).thenReturn(false)
+                }
+            val wifiEntry =
+                mock<WifiEntry>().apply {
+                    whenever(this.isPrimaryNetwork).thenReturn(true)
+                    whenever(this.level).thenReturn(1)
+                    whenever(this.title).thenReturn(TITLE)
+                }
+            whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+
+            getCallback().onWifiEntriesChanged()
+
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+        }
+
+    @Test
     fun secondaryNetworks_activeEntriesEmpty_isEmpty() =
         testScope.runTest {
             featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 1a8c583..30132f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.communal.data.repository
 
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
 import com.android.systemui.communal.shared.CommunalAppWidgetInfo
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -8,6 +9,7 @@
 class FakeCommunalWidgetRepository : CommunalWidgetRepository {
     private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
     override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
+    override var communalWidgetAllowlist: List<CommunalWidgetMetadata> = emptyList()
 
     fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
         _stopwatchAppWidgetInfo.value = appWidgetInfo
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index e91e955..85261123 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -34,7 +34,7 @@
         get() = _isFingerprintAuthCurrentlyAllowed
 
     private val _isFaceAuthEnrolledAndEnabled = MutableStateFlow(false)
-    override val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
+    override val isFaceAuthEnrolledAndEnabled: StateFlow<Boolean>
         get() = _isFaceAuthEnrolledAndEnabled
 
     private val _isFaceAuthCurrentlyAllowed = MutableStateFlow(false)
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index ee41a69..65975e4 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -1437,6 +1437,10 @@
                 if (!mDevCfgEnableContentProtectionReceiver) {
                     return false;
                 }
+                if (mDevCfgContentProtectionRequiredGroups.isEmpty()
+                        && mDevCfgContentProtectionOptionalGroups.isEmpty()) {
+                    return false;
+                }
             }
             return mContentProtectionConsentManager.isConsentGranted(userId)
                     && mContentProtectionBlocklistManager.isAllowed(packageName);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 8df5456..638abdb 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1387,11 +1387,12 @@
             @UserIdInt int userId);
 
     /**
-     * Tells PackageManager when a component (except BroadcastReceivers) of the package is used
+     * Tells PackageManager when a component of the package is used
      * and the package should get out of stopped state and be enabled.
      */
     public abstract void notifyComponentUsed(@NonNull String packageName,
-            @UserIdInt int userId, @NonNull String recentCallingPackage, @NonNull String debugInfo);
+            @UserIdInt int userId, @Nullable String recentCallingPackage,
+            @NonNull String debugInfo);
 
     /** @deprecated For legacy shell command only. */
     @Deprecated
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5fb889a..1650a96 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5309,7 +5309,7 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
             }
-            long flags = Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE;
+            long flags = Context.BIND_AUTO_CREATE;
             if (mAuthenticatorCache.getBindInstantServiceAllowed(mAccounts.userId)) {
                 flags |= Context.BIND_ALLOW_INSTANT;
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0956c6d..5f1a7e7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3678,8 +3678,8 @@
                 || (flags & Context.BIND_EXTERNAL_SERVICE_LONG) != 0;
         final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
         final boolean inSharedIsolatedProcess = (flags & Context.BIND_SHARED_ISOLATED_PROCESS) != 0;
-        final boolean filterOutQuarantined =
-                (flags & Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS) != 0;
+        final boolean matchQuarantined =
+                (flags & Context.BIND_MATCH_QUARANTINED_COMPONENTS) != 0;
 
         ProcessRecord attributedApp = null;
         if (sdkSandboxClientAppUid > 0) {
@@ -3689,7 +3689,7 @@
                 isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
                 resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg,
                 isBindExternal, allowInstant, null /* fgsDelegateOptions */,
-                inSharedIsolatedProcess, filterOutQuarantined);
+                inSharedIsolatedProcess, matchQuarantined);
         if (res == null) {
             return 0;
         }
@@ -4202,7 +4202,7 @@
                 sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage,
                 callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal,
                 allowInstant, fgsDelegateOptions, inSharedIsolatedProcess,
-                false /* filterOutQuarantined */);
+                false /* matchQuarantined */);
     }
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
@@ -4211,7 +4211,7 @@
             String callingPackage, int callingPid, int callingUid, int userId,
             boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
             boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions,
-            boolean inSharedIsolatedProcess, boolean filterOutQuarantined) {
+            boolean inSharedIsolatedProcess, boolean matchQuarantined) {
         if (isSdkSandboxService && instanceName == null) {
             throw new IllegalArgumentException("No instanceName provided for sdk sandbox process");
         }
@@ -4333,8 +4333,8 @@
                 if (allowInstant) {
                     flags |= PackageManager.MATCH_INSTANT;
                 }
-                if (filterOutQuarantined) {
-                    flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+                if (matchQuarantined) {
+                    flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
                 }
                 // TODO: come back and remove this assumption to triage all services
                 ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b43b986..31817f1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -58,7 +58,6 @@
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
 import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -14295,8 +14294,7 @@
     private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
             int callingUid, int[] users, int[] broadcastAllowList) {
         // TODO: come back and remove this assumption to triage all broadcasts
-        long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING
-                | FILTER_OUT_QUARANTINED_COMPONENTS;
+        long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
 
         List<ResolveInfo> receivers = null;
         HashSet<ComponentName> singleUserReceivers = null;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 127c5b3..3c56752 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -1440,10 +1440,9 @@
                     r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
         }
 
-        // Broadcast is being executed, its package can't be stopped.
         try {
-            mService.mPackageManagerInt.setPackageStoppedState(
-                    r.curComponent.getPackageName(), false, r.userId);
+            mService.mPackageManagerInt.notifyComponentUsed(
+                    r.curComponent.getPackageName(), r.userId, r.callerPackage, r.toString());
         } catch (IllegalArgumentException e) {
             Slog.w(TAG, "Failed trying to unstop package "
                     + r.curComponent.getPackageName() + ": " + e);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index d19eae5..b481697 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1982,8 +1982,8 @@
         mService.notifyPackageUse(receiverPackageName,
                 PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
 
-        mService.mPackageManagerInt.setPackageStoppedState(
-                receiverPackageName, false, r.userId);
+        mService.mPackageManagerInt.notifyComponentUsed(
+                receiverPackageName, r.userId, r.callerPackage, r.toString());
     }
 
     private void reportUsageStatsBroadcastDispatched(@NonNull ProcessRecord app,
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 3771c05..f02b8c7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -511,8 +511,8 @@
         pw.print(prefix); pw.print("pid="); pw.println(mPid);
         pw.print(prefix); pw.print("lastActivityTime=");
         TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
-        pw.print(prefix); pw.print("startUptimeTime=");
-        TimeUtils.formatDuration(mStartElapsedTime, nowUptime, pw);
+        pw.print(prefix); pw.print("startUpTime=");
+        TimeUtils.formatDuration(mStartUptime, nowUptime, pw);
         pw.print(prefix); pw.print("startElapsedTime=");
         TimeUtils.formatDuration(mStartElapsedTime, nowElapsedTime, pw);
         pw.println();
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 16e3fdf2..2d231b3 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -152,6 +152,7 @@
         "preload_safety",
         "responsible_apis",
         "rust",
+        "safety_center",
         "system_performance",
         "test_suites",
         "text",
@@ -159,7 +160,14 @@
         "tv_system_ui",
         "vibrator",
         "virtual_devices",
+        "wear_calling_messaging",
+        "wear_connectivity",
+        "wear_esim_carriers",
         "wear_frameworks",
+        "wear_health_services",
+        "wear_media",
+        "wear_offload",
+        "wear_security",
         "wear_system_health",
         "wear_systems",
         "window_surfaces",
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8736a53..ac7d9c1 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -221,9 +221,8 @@
 
     /** Flags used when connecting to a sync adapter service */
     private static final Context.BindServiceFlags SYNC_ADAPTER_CONNECTION_FLAGS =
-            Context.BindServiceFlags.of(
-                    Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE
-                            | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT);
+            Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+                    | Context.BIND_ALLOW_OOM_MANAGEMENT);
 
     /** Singleton instance. */
     @GuardedBy("SyncManager.class")
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0689478..5b87eea 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -494,6 +494,7 @@
 
     // If we would like to keep a particular eye on a package, we can set the package name.
     private final boolean mExtraDisplayEventLogging;
+    private final String mExtraDisplayLoggingPackageName;
 
     private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
         @Override
@@ -584,8 +585,8 @@
         mOverlayProperties = SurfaceControl.getOverlaySupport();
         mSystemReady = false;
         mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
-        final String name = DisplayProperties.debug_vri_package().orElse(null);
-        mExtraDisplayEventLogging = !TextUtils.isEmpty(name);
+        mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
+        mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
     }
 
     public void setupSchedulerPolicies() {
@@ -757,7 +758,8 @@
 
         mContext.registerReceiver(mIdleModeReceiver, filter);
 
-        mSmallAreaDetectionController = SmallAreaDetectionController.create(mContext);
+        mSmallAreaDetectionController = (mFlags.isSmallAreaDetectionEnabled())
+                ? SmallAreaDetectionController.create(mContext) : null;
     }
 
     @VisibleForTesting
@@ -2934,12 +2936,17 @@
         // Only send updates outside of DisplayManagerService for enabled displays
         if (display.isEnabledLocked()) {
             sendDisplayEventLocked(display, event);
+        } else if (mExtraDisplayEventLogging) {
+            Slog.i(TAG, "Not Sending Display Event; display is not enabled: " + display);
         }
     }
 
     private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) {
         int displayId = display.getDisplayIdLocked();
         Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+        if (mExtraDisplayEventLogging) {
+            Slog.i(TAG, "Deliver Display Event on Handler: " + event);
+        }
         mHandler.sendMessage(msg);
     }
 
@@ -3005,6 +3012,10 @@
                 // For cached apps, save the pending event until it becomes non-cached
                 synchronized (mPendingCallbackSelfLocked) {
                     PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid);
+                    if (extraLogging(callbackRecord.mPackageName)) {
+                        Slog.i(TAG,
+                                "Uid is cached: " + uid + ", pendingCallback: " + pendingCallback);
+                    }
                     if (pendingCallback == null) {
                         mPendingCallbackSelfLocked.put(uid,
                                 new PendingCallback(callbackRecord, displayId, event));
@@ -3019,6 +3030,10 @@
         mTempCallbacks.clear();
     }
 
+    private boolean extraLogging(String packageName) {
+        return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
+    }
+
     // Runs on Handler thread.
     // Delivers display group event notifications to callbacks.
     private void deliverDisplayGroupEvent(int groupId, int event) {
@@ -3462,6 +3477,7 @@
         public final int mUid;
         private final IDisplayManagerCallback mCallback;
         private @EventsMask AtomicLong mEventsMask;
+        private final String mPackageName;
 
         public boolean mWifiDisplayScanRequested;
 
@@ -3471,6 +3487,9 @@
             mUid = uid;
             mCallback = callback;
             mEventsMask = new AtomicLong(eventsMask);
+
+            String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+            mPackageName = packageNames == null ? null : packageNames[0];
         }
 
         public void updateEventsMask(@EventsMask long eventsMask) {
@@ -3479,7 +3498,8 @@
 
         @Override
         public void binderDied() {
-            if (DEBUG) {
+            if (DEBUG || mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(
+                    mPackageName)) {
                 Slog.d(TAG, "Display listener for pid " + mPid + " died.");
             }
             onCallbackDied(this);
@@ -3490,6 +3510,11 @@
          */
         public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
             if (!shouldSendEvent(event)) {
+                if (mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(
+                        mPackageName)) {
+                    Slog.i(TAG,
+                            "Not sending displayEvent: " + event + " due to mask:" + mEventsMask);
+                }
                 return true;
             }
 
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
index adaa539..bf384b0 100644
--- a/services/core/java/com/android/server/display/SmallAreaDetectionController.java
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
 import android.util.ArrayMap;
@@ -30,15 +31,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.Map;
 
 final class SmallAreaDetectionController {
-    private static native void nativeUpdateSmallAreaDetection(int[] uids, float[] thresholds);
-    private static native void nativeSetSmallAreaDetectionThreshold(int uid, float threshold);
+    private static native void nativeUpdateSmallAreaDetection(int[] appIds, float[] thresholds);
+    private static native void nativeSetSmallAreaDetectionThreshold(int appId, float threshold);
 
     // TODO(b/281720315): Move this to DeviceConfig once server side ready.
     private static final String KEY_SMALL_AREA_DETECTION_ALLOWLIST =
@@ -47,12 +47,8 @@
     private final Object mLock = new Object();
     private final Context mContext;
     private final PackageManagerInternal mPackageManager;
-    private final UserManagerInternal mUserManager;
     @GuardedBy("mLock")
     private final Map<String, Float> mAllowPkgMap = new ArrayMap<>();
-    // TODO(b/298722189): Update allowlist when user changes
-    @GuardedBy("mLock")
-    private int[] mUserIds;
 
     static SmallAreaDetectionController create(@NonNull Context context) {
         final SmallAreaDetectionController controller =
@@ -67,7 +63,6 @@
     SmallAreaDetectionController(Context context, DeviceConfigInterface deviceConfig) {
         mContext = context;
         mPackageManager = LocalServices.getService(PackageManagerInternal.class);
-        mUserManager = LocalServices.getService(UserManagerInternal.class);
         deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                 BackgroundThread.getExecutor(),
                 new SmallAreaDetectionController.OnPropertiesChangedListener());
@@ -76,6 +71,7 @@
 
     @VisibleForTesting
     void updateAllowlist(@Nullable String property) {
+        final Map<String, Float> allowPkgMap = new ArrayMap<>();
         synchronized (mLock) {
             mAllowPkgMap.clear();
             if (property != null) {
@@ -86,8 +82,11 @@
                         .getStringArray(R.array.config_smallAreaDetectionAllowlist);
                 for (String defaultMapString : defaultMapStrings) putToAllowlist(defaultMapString);
             }
-            updateSmallAreaDetection();
+
+            if (mAllowPkgMap.isEmpty()) return;
+            allowPkgMap.putAll(mAllowPkgMap);
         }
+        updateSmallAreaDetection(allowPkgMap);
     }
 
     @GuardedBy("mLock")
@@ -105,43 +104,32 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void updateUidListForAllUsers(SparseArray<Float> list, String pkg, float threshold) {
-        for (int i = 0; i < mUserIds.length; i++) {
-            final int userId = mUserIds[i];
-            final int uid = mPackageManager.getPackageUid(pkg, 0, userId);
-            if (uid > 0) list.put(uid, threshold);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void updateSmallAreaDetection() {
-        if (mAllowPkgMap.isEmpty()) return;
-
-        mUserIds = mUserManager.getUserIds();
-
-        final SparseArray<Float> uidThresholdList = new SparseArray<>();
-        for (String pkg : mAllowPkgMap.keySet()) {
-            final float threshold = mAllowPkgMap.get(pkg);
-            updateUidListForAllUsers(uidThresholdList, pkg, threshold);
+    private void updateSmallAreaDetection(Map<String, Float> allowPkgMap) {
+        final SparseArray<Float> appIdThresholdList = new SparseArray(allowPkgMap.size());
+        for (String pkg : allowPkgMap.keySet()) {
+            final float threshold = allowPkgMap.get(pkg);
+            final PackageStateInternal stage = mPackageManager.getPackageStateInternal(pkg);
+            if (stage != null) {
+                appIdThresholdList.put(stage.getAppId(), threshold);
+            }
         }
 
-        final int[] uids = new int[uidThresholdList.size()];
-        final float[] thresholds = new float[uidThresholdList.size()];
-        for (int i = 0; i < uidThresholdList.size();  i++) {
-            uids[i] = uidThresholdList.keyAt(i);
-            thresholds[i] = uidThresholdList.valueAt(i);
+        final int[] appIds = new int[appIdThresholdList.size()];
+        final float[] thresholds = new float[appIdThresholdList.size()];
+        for (int i = 0; i < appIdThresholdList.size();  i++) {
+            appIds[i] = appIdThresholdList.keyAt(i);
+            thresholds[i] = appIdThresholdList.valueAt(i);
         }
-        updateSmallAreaDetection(uids, thresholds);
+        updateSmallAreaDetection(appIds, thresholds);
     }
 
     @VisibleForTesting
-    void updateSmallAreaDetection(int[] uids, float[] thresholds) {
-        nativeUpdateSmallAreaDetection(uids, thresholds);
+    void updateSmallAreaDetection(int[] appIds, float[] thresholds) {
+        nativeUpdateSmallAreaDetection(appIds, thresholds);
     }
 
-    void setSmallAreaDetectionThreshold(int uid, float threshold) {
-        nativeSetSmallAreaDetectionThreshold(uid, threshold);
+    void setSmallAreaDetectionThreshold(int appId, float threshold) {
+        nativeSetSmallAreaDetectionThreshold(appId, threshold);
     }
 
     void dump(PrintWriter pw) {
@@ -151,7 +139,6 @@
             for (String pkg : mAllowPkgMap.keySet()) {
                 pw.println("    " + pkg + " threshold = " + mAllowPkgMap.get(pkg));
             }
-            pw.println("  mUserIds=" + Arrays.toString(mUserIds));
         }
     }
 
@@ -167,11 +154,15 @@
     private final class PackageReceiver implements PackageManagerInternal.PackageListObserver {
         @Override
         public void onPackageAdded(@NonNull String packageName, int uid) {
+            float threshold = 0.0f;
             synchronized (mLock) {
                 if (mAllowPkgMap.containsKey(packageName)) {
-                    setSmallAreaDetectionThreshold(uid, mAllowPkgMap.get(packageName));
+                    threshold = mAllowPkgMap.get(packageName);
                 }
             }
+            if (threshold > 0.0f) {
+                setSmallAreaDetectionThreshold(UserHandle.getAppId(uid), threshold);
+            }
         }
     }
 }
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 fae8383..d953e8e 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -71,6 +71,10 @@
             Flags.FLAG_ENABLE_POWER_THROTTLING_CLAMPER,
             Flags::enablePowerThrottlingClamper);
 
+    private final FlagState mSmallAreaDetectionFlagState = new FlagState(
+            Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
+            Flags::enableSmallAreaDetection);
+
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
         return mConnectedDisplayManagementFlagState.isEnabled();
@@ -147,6 +151,10 @@
         return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled();
     }
 
+    public boolean isSmallAreaDetectionEnabled() {
+        return mSmallAreaDetectionFlagState.isEnabled();
+    }
+
     private static class FlagState {
 
         private final String mName;
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 9ab9c9d..9141814 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
@@ -104,3 +104,12 @@
     bug: "211737588"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "enable_small_area_detection"
+    namespace: "display_manager"
+    description: "Feature flag for SmallAreaDetection"
+    bug: "298722189"
+    is_fixed_read_only: true
+}
+
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index ca23844..d023913 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -723,7 +723,9 @@
             if (mode.getPhysicalWidth() > maxAllowedWidth
                     || mode.getPhysicalHeight() > maxAllowedHeight
                     || mode.getPhysicalWidth() < outSummary.minWidth
-                    || mode.getPhysicalHeight() < outSummary.minHeight) {
+                    || mode.getPhysicalHeight() < outSummary.minHeight
+                    || mode.getRefreshRate() < outSummary.minPhysicalRefreshRate
+                    || mode.getRefreshRate() > outSummary.maxPhysicalRefreshRate) {
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
new file mode 100644
index 0000000..ff70cb3
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.projection;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+
+public class MediaProjectionSessionIdGenerator {
+
+    private static final String PREFERENCES_FILE_NAME = "media_projection_session_id";
+    private static final String SESSION_ID_PREF_KEY = "media_projection_session_id_key";
+    private static final int SESSION_ID_DEFAULT_VALUE = 0;
+
+    private static final Object sInstanceLock = new Object();
+
+    @GuardedBy("sInstanceLock")
+    private static MediaProjectionSessionIdGenerator sInstance;
+
+    private final Object mSessionIdLock = new Object();
+
+    @GuardedBy("mSessionIdLock")
+    private final SharedPreferences mSharedPreferences;
+
+    /** Creates or returns an existing instance of {@link MediaProjectionSessionIdGenerator}. */
+    public static MediaProjectionSessionIdGenerator getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                File preferencesFile =
+                        new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+                SharedPreferences preferences =
+                        context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+                sInstance = new MediaProjectionSessionIdGenerator(preferences);
+            }
+            return sInstance;
+        }
+    }
+
+    @VisibleForTesting
+    public MediaProjectionSessionIdGenerator(SharedPreferences sharedPreferences) {
+        this.mSharedPreferences = sharedPreferences;
+    }
+
+    /** Returns the current session ID. This value is persisted across reboots. */
+    public int getCurrentSessionId() {
+        synchronized (mSessionIdLock) {
+            return getCurrentSessionIdInternal();
+        }
+    }
+
+    /**
+     * Creates and returns a new session ID. This value will be persisted as the new current session
+     * ID, and will be persisted across reboots.
+     */
+    public int createAndGetNewSessionId() {
+        synchronized (mSessionIdLock) {
+            int newSessionId = getCurrentSessionId() + 1;
+            setSessionIdInternal(newSessionId);
+            return newSessionId;
+        }
+    }
+
+    @GuardedBy("mSessionIdLock")
+    private void setSessionIdInternal(int value) {
+        mSharedPreferences.edit().putInt(SESSION_ID_PREF_KEY, value).apply();
+    }
+
+    @GuardedBy("mSessionIdLock")
+    private int getCurrentSessionIdInternal() {
+        return mSharedPreferences.getInt(SESSION_ID_PREF_KEY, SESSION_ID_DEFAULT_VALUE);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 7db7bf5..30017be 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -505,6 +505,10 @@
             int filterCallingUid, int userId, boolean resolveForStart,
             boolean allowDynamicSplits) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
+
+        // Allow to match activities of quarantined packages.
+        flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
+
         final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */,
@@ -647,11 +651,6 @@
         flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
 
-        // Only if the query is coming from the system process,
-        // it should be allowed to match quarantined components
-        if (callingUid != Process.SYSTEM_UID) {
-            flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
-        }
         Intent originalIntent = null;
         ComponentName comp = intent.getComponent();
         if (comp == null) {
@@ -4047,9 +4046,6 @@
         flags = updateFlagsForComponent(flags, userId);
         enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                 false /* checkShell */, "get provider info");
-        if (callingUid != Process.SYSTEM_UID) {
-            flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
-        }
         ParsedProvider p = mComponentResolver.getProvider(component);
         if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getProviderInfo " + component + ": " + p);
@@ -4679,9 +4675,6 @@
             int callingUid) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId);
-        if (callingUid != Process.SYSTEM_UID) {
-            flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
-        }
         final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
                 userId);
         boolean checkedGrants = false;
@@ -4794,13 +4787,6 @@
                 false /* checkShell */, "queryContentProviders");
         if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForComponent(flags, userId);
-
-        // Only if the service query is coming from the system process,
-        // it should be allowed to match quarantined components
-        if (callingUid != Process.SYSTEM_UID) {
-            flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
-        }
-
         ArrayList<ProviderInfo> finalList = null;
         final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
                 metaDataKey, uid, flags, userId);
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 148e0df..9ad8318 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -89,6 +89,21 @@
         if (packageState == null || packageState.getPkg() == null) {
             throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
         }
+        final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
+                mPm.mUserManager.getUserIds(), true);
+        final UserHandle userForMove;
+        if (installedUserIds.length > 0) {
+            userForMove = UserHandle.of(installedUserIds[0]);
+        } else {
+            throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST,
+                    "Package is not installed for any user");
+        }
+        for (int userId : installedUserIds) {
+            if (snapshot.shouldFilterApplicationIncludingUninstalled(packageState, callingUid,
+                    userId)) {
+                throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
+            }
+        }
         final AndroidPackage pkg = packageState.getPkg();
         if (packageState.isSystem()) {
             throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
@@ -134,8 +149,6 @@
         final String label = String.valueOf(pm.getApplicationLabel(
                 AndroidPackageUtils.generateAppInfoWithoutState(pkg)));
         final int targetSdkVersion = pkg.getTargetSdkVersion();
-        final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
-                mPm.mUserManager.getUserIds(), true);
         final String fromCodePath;
         if (codeFile.getParentFile().getName().startsWith(
                 PackageManagerService.RANDOM_DIR_PREFIX)) {
@@ -303,8 +316,8 @@
         final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
         final InstallingSession installingSession = new InstallingSession(origin, move,
                 installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
-                volumeUuid, user, packageAbiOverride,  PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
-                lite, mPm);
+                volumeUuid, userForMove, packageAbiOverride,
+                PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
         installingSession.movePackage();
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index f59188e..0fb1f7a 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -196,8 +196,12 @@
             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);
+                ArchiveActivityInfo activityInfo =
+                        new ArchiveActivityInfo(
+                                mainActivity.title,
+                                mainActivity.originalComponentName,
+                                iconPath,
+                                null);
                 archiveActivityInfos.add(activityInfo);
             }
 
@@ -215,8 +219,12 @@
         for (int i = 0, size = mainActivities.size(); i < size; i++) {
             LauncherActivityInfo mainActivity = mainActivities.get(i);
             Path iconPath = storeIcon(packageName, mainActivity, userId, i);
-            ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
-                    mainActivity.getLabel().toString(), iconPath, null);
+            ArchiveActivityInfo activityInfo =
+                    new ArchiveActivityInfo(
+                            mainActivity.getLabel().toString(),
+                            mainActivity.getComponentName(),
+                            iconPath,
+                            null);
             archiveActivityInfos.add(activityInfo);
         }
 
@@ -593,6 +601,7 @@
             }
             var archivedActivity = new ArchivedActivityParcel();
             archivedActivity.title = info.getTitle();
+            archivedActivity.originalComponentName = info.getOriginalComponentName();
             archivedActivity.iconBitmap = bytesFromBitmapFile(info.getIconBitmap());
             archivedActivity.monochromeIconBitmap = bytesFromBitmapFile(
                     info.getMonochromeIconBitmap());
@@ -624,6 +633,7 @@
             }
             var archivedActivity = new ArchivedActivityParcel();
             archivedActivity.title = info.getLabel().toString();
+            archivedActivity.originalComponentName = info.getComponentName();
             archivedActivity.iconBitmap =
                     info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap(
                             drawableToBitmap(info.getIcon(/* density= */ 0)));
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 651845e..e749968 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -747,7 +747,7 @@
 
     @Override
     public void notifyComponentUsed(@NonNull String packageName, @UserIdInt int userId,
-            @NonNull String recentCallingPackage, @NonNull String debugInfo) {
+            @Nullable String recentCallingPackage, @NonNull String debugInfo) {
         mService.notifyComponentUsed(snapshot(), packageName, userId,
                 recentCallingPackage, debugInfo);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index abeabc9..d0e2f01 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4576,7 +4576,7 @@
     }
 
     void notifyComponentUsed(@NonNull Computer snapshot, @NonNull String packageName,
-            @UserIdInt int userId, @NonNull String recentCallingPackage,
+            @UserIdInt int userId, @Nullable String recentCallingPackage,
             @NonNull String debugInfo) {
         synchronized (mLock) {
             final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9f4e86d..3ca933a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1228,6 +1228,9 @@
             long activityInfoToken = proto.start(
                     PackageProto.UserInfoProto.ArchiveState.ACTIVITY_INFOS);
             proto.write(ArchiveActivityInfo.TITLE, activityInfo.getTitle());
+            proto.write(
+                    ArchiveActivityInfo.ORIGINAL_COMPONENT_NAME,
+                    activityInfo.getOriginalComponentName().flattenToString());
             if (activityInfo.getIconBitmap() != null) {
                 proto.write(ArchiveActivityInfo.ICON_BITMAP_PATH,
                         activityInfo.getIconBitmap().toAbsolutePath().toString());
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index da14397..203e1de 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -517,12 +517,6 @@
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
 
-        // Only if the service query is coming from the system process,
-        // it should be allowed to match quarantined components
-        if (callingUid != Process.SYSTEM_UID) {
-            flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
-        }
-
         final String instantAppPkgName = computer.getInstantAppPackageName(callingUid);
         flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 451b3a5..e726d91 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -368,6 +368,7 @@
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_FIRST_INSTALL_TIME = "first-install-time";
     private static final String ATTR_ARCHIVE_ACTIVITY_TITLE = "activity-title";
+    private static final String ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME = "original-component-name";
     private static final String ATTR_ARCHIVE_INSTALLER_TITLE = "installer-title";
     private static final String ATTR_ARCHIVE_ICON_PATH = "icon-path";
     private static final String ATTR_ARCHIVE_MONOCHROME_ICON_PATH = "monochrome-icon-path";
@@ -2079,6 +2080,8 @@
             if (tagName.equals(TAG_ARCHIVE_ACTIVITY_INFO)) {
                 String title = parser.getAttributeValue(null,
                         ATTR_ARCHIVE_ACTIVITY_TITLE);
+                String originalComponentName =
+                        parser.getAttributeValue(null, ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME);
                 String iconAttribute = parser.getAttributeValue(null,
                         ATTR_ARCHIVE_ICON_PATH);
                 Path iconPath = iconAttribute == null ? null : Path.of(iconAttribute);
@@ -2087,17 +2090,27 @@
                 Path monochromeIconPath = monochromeAttribute == null ? null : Path.of(
                         monochromeAttribute);
 
-                if (title == null || iconPath == null) {
-                    Slog.wtf(TAG,
-                            TextUtils.formatSimple("Missing attributes in tag %s. %s: %s, %s: %s",
-                                    TAG_ARCHIVE_ACTIVITY_INFO, ATTR_ARCHIVE_ACTIVITY_TITLE, title,
+                if (title == null || originalComponentName == null || iconPath == null) {
+                    Slog.wtf(
+                            TAG,
+                            TextUtils.formatSimple(
+                                    "Missing attributes in tag %s. %s: %s, %s: %s, %s: %s",
+                                    TAG_ARCHIVE_ACTIVITY_INFO,
+                                    ATTR_ARCHIVE_ACTIVITY_TITLE,
+                                    title,
+                                    ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME,
+                                    originalComponentName,
                                     ATTR_ARCHIVE_ICON_PATH,
                                     iconPath));
                     continue;
                 }
 
                 activityInfos.add(
-                        new ArchiveState.ArchiveActivityInfo(title, iconPath, monochromeIconPath));
+                        new ArchiveState.ArchiveActivityInfo(
+                                title,
+                                ComponentName.unflattenFromString(originalComponentName),
+                                iconPath,
+                                monochromeIconPath));
             }
         }
         return activityInfos;
@@ -2469,6 +2482,10 @@
         for (ArchiveState.ArchiveActivityInfo activityInfo : archiveState.getActivityInfos()) {
             serializer.startTag(null, TAG_ARCHIVE_ACTIVITY_INFO);
             serializer.attribute(null, ATTR_ARCHIVE_ACTIVITY_TITLE, activityInfo.getTitle());
+            serializer.attribute(
+                    null,
+                    ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME,
+                    activityInfo.getOriginalComponentName().flattenToString());
             if (activityInfo.getIconBitmap() != null) {
                 serializer.attribute(null, ATTR_ARCHIVE_ICON_PATH,
                         activityInfo.getIconBitmap().toAbsolutePath().toString());
@@ -6411,17 +6428,26 @@
     }
 
     boolean clearPersistentPreferredActivity(IntentFilter filter, int userId) {
+        ArrayList<PersistentPreferredActivity> removed = null;
         PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
         Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
         boolean changed = false;
         while (it.hasNext()) {
             PersistentPreferredActivity ppa = it.next();
             if (IntentFilter.filterEquals(ppa.getIntentFilter(), filter)) {
-                ppir.removeFilter(ppa);
-                changed = true;
-                break;
+                if (removed == null) {
+                    removed = new ArrayList<>();
+                }
+                removed.add(ppa);
             }
         }
+        if (removed != null) {
+            for (int i = 0; i < removed.size(); i++) {
+                PersistentPreferredActivity ppa = removed.get(i);
+                ppir.removeFilter(ppa);
+            }
+            changed = true;
+        }
         if (changed) {
             onChanged();
         }
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 d804e01..61e96ca 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -379,7 +379,7 @@
         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
+        if ((flags & PackageManager.MATCH_QUARANTINED_COMPONENTS) == 0
                 && state.isQuarantined()) {
             ai.enabled = false;
         } else  if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
diff --git a/services/core/java/com/android/server/pm/pkg/ArchiveState.java b/services/core/java/com/android/server/pm/pkg/ArchiveState.java
index 4916a4a..1e40d44 100644
--- a/services/core/java/com/android/server/pm/pkg/ArchiveState.java
+++ b/services/core/java/com/android/server/pm/pkg/ArchiveState.java
@@ -16,9 +16,11 @@
 
 package com.android.server.pm.pkg;
 
+import android.content.ComponentName;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import com.android.internal.util.AnnotationValidations;
 import com.android.internal.util.DataClass;
 
 import java.nio.file.Path;
@@ -56,6 +58,10 @@
         @NonNull
         private final String mTitle;
 
+        /** The component name of the original activity (pre-archival). */
+        @NonNull
+        private final ComponentName mOriginalComponentName;
+
         /**
          * The path to the stored icon of the activity in the app's locale. Null if the app does
          * not define any icon (default icon would be shown on the launcher).
@@ -96,11 +102,13 @@
         @DataClass.Generated.Member
         public ArchiveActivityInfo(
                 @NonNull String title,
+                @NonNull ComponentName originalComponentName,
                 @Nullable Path iconBitmap,
                 @Nullable Path monochromeIconBitmap) {
             this.mTitle = title;
-            com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mTitle);
+            this.mOriginalComponentName = originalComponentName;
+            AnnotationValidations.validate(NonNull.class, null, mTitle);
+            AnnotationValidations.validate(NonNull.class, null, mOriginalComponentName);
             this.mIconBitmap = iconBitmap;
             this.mMonochromeIconBitmap = monochromeIconBitmap;
 
@@ -116,6 +124,14 @@
         }
 
         /**
+         * The component name of the original activity (pre-archival).
+            */
+        @DataClass.Generated.Member
+        public @NonNull ComponentName getOriginalComponentName() {
+            return mOriginalComponentName;
+        }
+
+        /**
          * The path to the stored icon of the activity in the app's locale. Null if the app does
          * not define any icon (default icon would be shown on the launcher).
          */
@@ -140,6 +156,7 @@
 
             return "ArchiveActivityInfo { " +
                     "title = " + mTitle + ", " +
+                    "originalComponentName = " + mOriginalComponentName + ", " +
                     "iconBitmap = " + mIconBitmap + ", " +
                     "monochromeIconBitmap = " + mMonochromeIconBitmap +
             " }";
@@ -159,6 +176,7 @@
             //noinspection PointlessBooleanExpression
             return true
                     && java.util.Objects.equals(mTitle, that.mTitle)
+                    && java.util.Objects.equals(mOriginalComponentName, that.mOriginalComponentName)
                     && java.util.Objects.equals(mIconBitmap, that.mIconBitmap)
                     && java.util.Objects.equals(mMonochromeIconBitmap, that.mMonochromeIconBitmap);
         }
@@ -171,6 +189,7 @@
 
             int _hash = 1;
             _hash = 31 * _hash + java.util.Objects.hashCode(mTitle);
+            _hash = 31* _hash + java.util.Objects.hashCode(mOriginalComponentName);
             _hash = 31 * _hash + java.util.Objects.hashCode(mIconBitmap);
             _hash = 31 * _hash + java.util.Objects.hashCode(mMonochromeIconBitmap);
             return _hash;
@@ -180,7 +199,8 @@
                 time = 1693590309015L,
                 codegenVersion = "1.0.23",
                 sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/ArchiveState.java",
-                inputSignatures = "private final @android.annotation.NonNull java.lang.String mTitle\nprivate final @android.annotation.Nullable java.nio.file.Path mIconBitmap\nprivate final @android.annotation.Nullable java.nio.file.Path mMonochromeIconBitmap\nclass ArchiveActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)")
+                inputSignatures =
+                        "private final @android.annotation.NonNull java.lang.String mTitle\nprivate final @android.annotation.NonNull android.content.ComponentName mOriginalComponentName\nprivate final @android.annotation.Nullable java.nio.file.Path mIconBitmap\nprivate final @android.annotation.Nullable java.nio.file.Path mMonochromeIconBitmap\nclass ArchiveActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)")
         @Deprecated
         private void __metadata() {}
 
@@ -224,11 +244,9 @@
             @NonNull List<ArchiveActivityInfo> activityInfos,
             @NonNull String installerTitle) {
         this.mActivityInfos = activityInfos;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mActivityInfos);
+        AnnotationValidations.validate(NonNull.class, null, mActivityInfos);
         this.mInstallerTitle = installerTitle;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mInstallerTitle);
+        AnnotationValidations.validate(NonNull.class, null, mInstallerTitle);
 
         // onConstructed(); // You can define this method to get a callback
     }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
index 7b07e5b..cd3583b 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -16,9 +16,9 @@
 
 package com.android.server.pm.pkg;
 
-import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_QUARANTINED_COMPONENTS;
 
 import android.annotation.NonNull;
 import android.content.pm.ComponentInfo;
@@ -147,7 +147,7 @@
             return true;
         }
 
-        if ((flags & FILTER_OUT_QUARANTINED_COMPONENTS) != 0 && state.isQuarantined()) {
+        if ((flags & MATCH_QUARANTINED_COMPONENTS) == 0 && state.isQuarantined()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index 174ad3a..c33f3d9 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,11 +1,10 @@
 jeffreyhuang@google.com
 joeo@google.com
-jtnguyen@google.com
+monicamwang@google.com
 muhammadq@google.com
+rayhdez@google.com
 rslawik@google.com
-ruchirr@google.com
 sharaienko@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
-yro@google.com
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 0718f2f..4200fbf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3982,7 +3982,9 @@
         if (wallpaper == null) {
             // common case, this is the first lookup post-boot of the system or
             // unified lock, so we bring up the saved state lazily now and recheck.
-            int whichLoad = (which == FLAG_LOCK) ? FLAG_LOCK : FLAG_SYSTEM;
+            // if we're loading the system wallpaper for the first time, also load the lock
+            // wallpaper to determine if the system wallpaper is system+lock or system only.
+            int whichLoad = (which == FLAG_LOCK) ? FLAG_LOCK : FLAG_SYSTEM | FLAG_LOCK;
             loadSettingsLocked(userId, false, whichLoad);
             wallpaper = whichSet.get(userId);
             if (wallpaper == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0b67321..de335d3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -68,6 +68,7 @@
 import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -3686,6 +3687,11 @@
                         getTransitionController(), mWindowManager.mSyncEngine)
                 : null;
 
+        if (r.getTaskFragment() != null && r.getTaskFragment().isEmbeddedWithBoundsOverride()
+                && transition != null) {
+            transition.addFlag(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
+        }
+
         final Runnable enterPipRunnable = () -> {
             synchronized (mGlobalLock) {
                 if (r.getParent() == null) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 4237668..6d59b29 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -276,6 +276,10 @@
                 // activity, we won't close the activity.
                 backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
                 removedWindowContainer = window;
+            } else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) {
+                // skip if current activity is translucent
+                backType = BackNavigationInfo.TYPE_CALLBACK;
+                removedWindowContainer = window;
             } else if (prevActivity != null) {
                 if (!isOccluded || prevActivity.canShowWhenLocked()) {
                     // We have another Activity in the same currentTask to go to
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 016b0ff..4922e90 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -489,10 +489,6 @@
 
     private boolean mForceShowForAllUsers;
 
-    /** When set, will force the task to report as invisible. */
-    static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
-    static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
-    private int mForceHiddenFlags = 0;
     private boolean mForceTranslucent = false;
 
     // The display category name for this task.
@@ -4495,20 +4491,13 @@
      * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
      * @return Whether the force hidden state changed
      */
-    boolean setForceHidden(int flags, boolean set) {
-        int newFlags = mForceHiddenFlags;
-        if (set) {
-            newFlags |= flags;
-        } else {
-            newFlags &= ~flags;
-        }
-        if (mForceHiddenFlags == newFlags) {
-            return false;
-        }
-
+    @Override
+    boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
         final boolean wasHidden = isForceHidden();
         final boolean wasVisible = isVisible();
-        mForceHiddenFlags = newFlags;
+        if (!super.setForceHidden(flags, set)) {
+            return false;
+        }
         final boolean nowHidden = isForceHidden();
         if (wasHidden != nowHidden) {
             final String reason = "setForceHidden";
@@ -4539,11 +4528,6 @@
         return super.isAlwaysOnTop();
     }
 
-    @Override
-    protected boolean isForceHidden() {
-        return mForceHiddenFlags != 0;
-    }
-
     boolean isForceHiddenForPinnedTask() {
         return (mForceHiddenFlags & FLAG_FORCE_HIDDEN_FOR_PINNED_TASK) != 0;
     }
@@ -5651,6 +5635,8 @@
             if (noAnimation) {
                 mDisplayContent.prepareAppTransition(TRANSIT_NONE);
                 mTaskSupervisor.mNoAnimActivities.add(top);
+                mTransitionController.collect(top);
+                mTransitionController.setNoAnimation(top);
                 ActivityOptions.abort(options);
             } else {
                 updateTransitLocked(TRANSIT_TO_FRONT, options);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 7c5bc6e..906b3b5 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -362,6 +362,19 @@
      */
     private boolean mIsolatedNav;
 
+    /** When set, will force the task to report as invisible. */
+    static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
+    static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
+    static final int FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG = 1 << 2;
+
+    @IntDef(prefix = {"FLAG_FORCE_HIDDEN_"}, value = {
+            FLAG_FORCE_HIDDEN_FOR_PINNED_TASK,
+            FLAG_FORCE_HIDDEN_FOR_TASK_ORG,
+            FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG,
+    }, flag = true)
+    @interface FlagForceHidden {}
+    protected int mForceHiddenFlags = 0;
+
     final Point mLastSurfaceSize = new Point();
 
     private final Rect mTmpBounds = new Rect();
@@ -845,7 +858,25 @@
      * Returns whether this TaskFragment is currently forced to be hidden for any reason.
      */
     protected boolean isForceHidden() {
-        return false;
+        return mForceHiddenFlags != 0;
+    }
+
+    /**
+     * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
+     * @return Whether the force hidden state changed
+     */
+    boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
+        int newFlags = mForceHiddenFlags;
+        if (set) {
+            newFlags |= flags;
+        } else {
+            newFlags &= ~flags;
+        }
+        if (mForceHiddenFlags == newFlags) {
+            return false;
+        }
+        mForceHiddenFlags = newFlags;
+        return true;
     }
 
     protected boolean isForceTranslucent() {
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 04164c2..ff766be 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -51,7 +51,6 @@
 import android.window.ITaskFragmentOrganizerController;
 import android.window.TaskFragmentInfo;
 import android.window.TaskFragmentOperation;
-import android.window.TaskFragmentOrganizerToken;
 import android.window.TaskFragmentParentInfo;
 import android.window.TaskFragmentTransaction;
 import android.window.WindowContainerTransaction;
@@ -745,9 +744,9 @@
         }
     }
 
-    boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) {
+    boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
         final TaskFragmentOrganizerState state =
-                mTaskFragmentOrganizerState.get(token.asBinder());
+                mTaskFragmentOrganizerState.get(organizerToken);
         return state != null && state.mIsSystemOrganizer;
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 93db1ca..7d65c61 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -3321,8 +3321,8 @@
             mFrozen.add(wc);
             final ChangeInfo changeInfo = Objects.requireNonNull(mChanges.get(wc));
             changeInfo.mSnapshot = snapshotSurface;
-            if (isDisplayRotation) {
-                // This isn't cheap, so only do it for display rotations.
+            if (changeInfo.mRotation != wc.mDisplayContent.getRotation()) {
+                // This isn't cheap, so only do it for rotation change.
                 changeInfo.mSnapshotLuma = TransitionAnimation.getBorderLuma(
                         buffer, screenshotBuffer.getColorSpace());
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9663f3a..88f72f9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8461,16 +8461,18 @@
                     return true;
                 }
                 // For a task session, find the activity identified by the launch cookie.
-                final WindowContainerToken wct = getTaskWindowContainerTokenForLaunchCookie(
+                final WindowContainerInfo wci = getTaskWindowContainerInfoForLaunchCookie(
                         incomingSession.getTokenToRecord());
-                if (wct == null) {
+                if (wci == null) {
                     Slog.w(TAG, "Handling a new recording session; unable to find the "
                             + "WindowContainerToken");
                     return false;
                 }
                 // Replace the launch cookie in the session details with the task's
                 // WindowContainerToken.
-                incomingSession.setTokenToRecord(wct.asBinder());
+                incomingSession.setTokenToRecord(wci.getToken().asBinder());
+                // Also replace the UNKNOWN target UID with the actual UID.
+                incomingSession.setTargetUid(wci.getUid());
                 mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
                         WindowManagerService.this);
                 return true;
@@ -8798,21 +8800,41 @@
         mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
     }
 
+    @VisibleForTesting
+    static class WindowContainerInfo {
+        private final int mUid;
+        @NonNull private final WindowContainerToken mToken;
+
+        private WindowContainerInfo(int uid, @NonNull WindowContainerToken token) {
+            this.mUid = uid;
+            this.mToken = token;
+        }
+
+        public int getUid() {
+            return mUid;
+        }
+
+        @NonNull
+        public WindowContainerToken getToken() {
+            return mToken;
+        }
+    }
+
     /**
-     * Retrieve the {@link WindowContainerToken} of the task that contains the activity started
-     * with the given launch cookie.
+     * Retrieve the {@link WindowContainerInfo} of the task that contains the activity started with
+     * the given launch cookie.
      *
      * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an
-     *                     activity
+     *     activity
      * @return a token representing the task containing the activity started with the given launch
-     * cookie, or {@code null} if the token couldn't be found.
+     *     cookie, or {@code null} if the token couldn't be found.
      */
     @VisibleForTesting
     @Nullable
-    WindowContainerToken getTaskWindowContainerTokenForLaunchCookie(@NonNull IBinder launchCookie) {
+    WindowContainerInfo getTaskWindowContainerInfoForLaunchCookie(@NonNull IBinder launchCookie) {
         // Find the activity identified by the launch cookie.
-        final ActivityRecord targetActivity = mRoot.getActivity(
-                activity -> activity.mLaunchCookie == launchCookie);
+        final ActivityRecord targetActivity =
+                mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie);
         if (targetActivity == null) {
             Slog.w(TAG, "Unable to find the activity for this launch cookie");
             return null;
@@ -8827,7 +8849,7 @@
             Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName());
             return null;
         }
-        return taskWindowContainerToken;
+        return new WindowContainerInfo(targetActivity.getUid(), taskWindowContainerToken);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index dd9a88f..5ed6caf 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -24,7 +24,9 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
@@ -34,6 +36,8 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
+import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE;
+import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN;
 import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION;
@@ -61,6 +65,7 @@
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
+import static com.android.server.wm.TaskFragment.FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -821,6 +826,7 @@
             return TRANSACT_EFFECTS_NONE;
         }
 
+        int effects = TRANSACT_EFFECTS_NONE;
         // When the TaskFragment is resized, we may want to create a change transition for it, for
         // which we want to defer the surface update until we determine whether or not to start
         // change transition.
@@ -843,7 +849,14 @@
             c.getConfiguration().windowConfiguration.setBounds(absBounds);
             taskFragment.setRelativeEmbeddedBounds(relBounds);
         }
-        final int effects = applyChanges(taskFragment, c);
+        if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+            if (taskFragment.setForceHidden(
+                    FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG, c.getHidden())) {
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
+        }
+        effects |= applyChanges(taskFragment, c);
+
         if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
             taskFragment.initializeChangeTransition(mTmpBounds0);
         }
@@ -1393,6 +1406,24 @@
                 taskFragment.setIsolatedNav(isolatedNav);
                 break;
             }
+            case OP_TYPE_REORDER_TO_BOTTOM_OF_TASK: {
+                final Task task = taskFragment.getTask();
+                if (task != null) {
+                    task.mChildren.remove(taskFragment);
+                    task.mChildren.add(0, taskFragment);
+                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                }
+                break;
+            }
+            case OP_TYPE_REORDER_TO_TOP_OF_TASK: {
+                final Task task = taskFragment.getTask();
+                if (task != null) {
+                    task.mChildren.remove(taskFragment);
+                    task.mChildren.add(taskFragment);
+                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                }
+                break;
+            }
         }
         return effects;
     }
@@ -1420,6 +1451,18 @@
             return false;
         }
 
+        if ((opType == OP_TYPE_REORDER_TO_BOTTOM_OF_TASK
+                || opType == OP_TYPE_REORDER_TO_TOP_OF_TASK)
+                && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+            final Throwable exception = new SecurityException(
+                    "Only a system organizer can perform OP_TYPE_REORDER_TO_BOTTOM_OF_TASK or "
+                            + "OP_TYPE_REORDER_TO_TOP_OF_TASK."
+            );
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+                    opType, exception);
+            return false;
+        }
+
         final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
         return secondaryFragmentToken == null
                 || validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
@@ -1920,6 +1963,11 @@
      * For config change on {@link TaskFragment}, we only support the following operations:
      * {@link WindowContainerTransaction#setRelativeBounds(WindowContainerToken, Rect)},
      * {@link WindowContainerTransaction#setWindowingMode(WindowContainerToken, int)}.
+     *
+     * For a system organizer, we additionally support
+     * {@link WindowContainerTransaction#setHidden(WindowContainerToken, boolean)}, and
+     * {@link WindowContainerTransaction#setFocusable(WindowContainerToken, boolean)}. See
+     * {@link TaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, boolean)}
      */
     private void enforceTaskFragmentConfigChangeAllowed(@NonNull String func,
             @Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change,
@@ -1938,31 +1986,49 @@
             throw new SecurityException(msg);
         }
 
-        final int changeMask = change.getChangeMask();
-        final int configSetMask = change.getConfigSetMask();
-        final int windowSetMask = change.getWindowSetMask();
-        if (changeMask == 0 && configSetMask == 0 && windowSetMask == 0
-                && change.getWindowingMode() >= 0) {
-            // The change contains only setWindowingMode, which is allowed.
-            return;
+        final int originalChangeMask = change.getChangeMask();
+        final int originalConfigSetMask = change.getConfigSetMask();
+        final int originalWindowSetMask = change.getWindowSetMask();
+
+        int changeMaskToBeChecked = originalChangeMask;
+        int configSetMaskToBeChecked = originalConfigSetMask;
+        int windowSetMaskToBeChecked = originalWindowSetMask;
+
+        if (mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+            // System organizer is allowed to update the hidden and focusable state.
+            // We unset the CHANGE_HIDDEN and CHANGE_FOCUSABLE bits because they are checked here.
+            changeMaskToBeChecked &= ~CHANGE_HIDDEN;
+            changeMaskToBeChecked &= ~CHANGE_FOCUSABLE;
         }
-        if (changeMask != CHANGE_RELATIVE_BOUNDS
-                || configSetMask != ActivityInfo.CONFIG_WINDOW_CONFIGURATION
-                || windowSetMask != WindowConfiguration.WINDOW_CONFIG_BOUNDS) {
-            // None of the change should be requested from a TaskFragment organizer except
-            // setRelativeBounds and setWindowingMode.
-            // For setRelativeBounds, we don't need to check whether it is outside of the Task
+
+        // setRelativeBounds is allowed.
+        if ((changeMaskToBeChecked & CHANGE_RELATIVE_BOUNDS) != 0
+                && (configSetMaskToBeChecked & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
+                && (windowSetMaskToBeChecked & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) {
+            // For setRelativeBounds, we don't need to check whether it is outside the Task
             // bounds, because it is possible that the Task is also resizing, for which we don't
             // want to throw an exception. The bounds will be adjusted in
             // TaskFragment#translateRelativeBoundsToAbsoluteBounds.
-            String msg = "Permission Denial: " + func + " from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " trying to apply changes of changeMask=" + changeMask
-                    + " configSetMask=" + configSetMask + " windowSetMask=" + windowSetMask
-                    + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
+            changeMaskToBeChecked &= ~CHANGE_RELATIVE_BOUNDS;
+            configSetMaskToBeChecked &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+            windowSetMaskToBeChecked &= ~WindowConfiguration.WINDOW_CONFIG_BOUNDS;
         }
+
+        if (changeMaskToBeChecked == 0 && configSetMaskToBeChecked == 0
+                && windowSetMaskToBeChecked == 0) {
+            // All the changes have been checked.
+            // Note that setWindowingMode is always allowed, so we don't need to check the mask.
+            return;
+        }
+
+        final String msg = "Permission Denial: " + func + " from pid="
+                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                + " trying to apply changes of changeMask=" + originalChangeMask
+                + " configSetMask=" + originalConfigSetMask
+                + " windowSetMask=" + originalWindowSetMask
+                + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
+        Slog.w(TAG, msg);
+        throw new SecurityException(msg);
     }
 
     private void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
@@ -2019,7 +2085,7 @@
         TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
         taskFragment.setTaskFragmentOrganizer(organizerToken,
                 ownerActivity.getUid(), ownerActivity.info.processName,
-                mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken));
+                mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder()));
         final int position;
         if (creationParams.getPairedPrimaryFragmentToken() != null) {
             // When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e434f29..7d21dbf 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -569,6 +569,7 @@
                 && asActivityRecord() != null && isVisible()) {
             // Trigger an activity level rotation transition.
             mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_CHANGE, this);
+            mTransitionController.collectVisibleChange(this);
             mTransitionController.setReady(this);
         }
         final int originalRotation = getWindowConfiguration().getRotation();
diff --git a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
index b256f16..1844d30 100644
--- a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
+++ b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
@@ -24,33 +24,33 @@
 #include "utils/Log.h"
 
 namespace android {
-static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray juids,
+static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray jappIds,
                                            jfloatArray jthresholds) {
-    if (juids == nullptr || jthresholds == nullptr) return;
+    if (jappIds == nullptr || jthresholds == nullptr) return;
 
-    ScopedIntArrayRO uids(env, juids);
+    ScopedIntArrayRO appIds(env, jappIds);
     ScopedFloatArrayRO thresholds(env, jthresholds);
 
-    if (uids.size() != thresholds.size()) {
-        ALOGE("uids size exceeds thresholds size!");
+    if (appIds.size() != thresholds.size()) {
+        ALOGE("appIds size exceeds thresholds size!");
         return;
     }
 
-    std::vector<int32_t> uidVector;
+    std::vector<int32_t> appIdVector;
     std::vector<float> thresholdVector;
-    size_t size = uids.size();
-    uidVector.reserve(size);
+    size_t size = appIds.size();
+    appIdVector.reserve(size);
     thresholdVector.reserve(size);
     for (int i = 0; i < size; i++) {
-        uidVector.push_back(static_cast<int32_t>(uids[i]));
+        appIdVector.push_back(static_cast<int32_t>(appIds[i]));
         thresholdVector.push_back(static_cast<float>(thresholds[i]));
     }
-    SurfaceComposerClient::updateSmallAreaDetection(uidVector, thresholdVector);
+    SurfaceComposerClient::updateSmallAreaDetection(appIdVector, thresholdVector);
 }
 
-static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint uid,
+static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint appId,
                                                  jfloat threshold) {
-    SurfaceComposerClient::setSmallAreaDetectionThreshold(uid, threshold);
+    SurfaceComposerClient::setSmallAreaDetectionThreshold(appId, threshold);
 }
 
 static const JNINativeMethod gMethods[] = {
diff --git a/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
index fc5a113..1e3cfd0 100644
--- a/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
@@ -15,106 +15,99 @@
  */
 
 #include <android_runtime/AndroidRuntime.h>
-#include <nativehelper/JNIHelp.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
 #include <jni.h>
+#include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-
-#include <utils/misc.h>
+#include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
 #include <utils/Log.h>
-
-
-#include <inttypes.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
+#include <utils/misc.h>
 
 namespace android {
 
-    uint64_t get_block_device_size(int fd)
-    {
-        uint64_t size = 0;
-        int ret;
+uint64_t get_block_device_size(int fd) {
+    uint64_t size = 0;
+    int ret;
 
-        ret = ioctl(fd, BLKGETSIZE64, &size);
+    ret = ioctl(fd, BLKGETSIZE64, &size);
 
-        if (ret)
-            return 0;
+    if (ret) return 0;
 
-        return size;
-    }
+    return size;
+}
 
-    int wipe_block_device(int fd)
-    {
-        uint64_t range[2];
-        int ret;
-        uint64_t len = get_block_device_size(fd);
+int wipe_block_device(int fd) {
+    uint64_t range[2];
+    int ret;
+    uint64_t len = get_block_device_size(fd);
 
+    range[0] = 0;
+    range[1] = len;
+
+    if (range[1] == 0) return 0;
+
+    ret = ioctl(fd, BLKSECDISCARD, &range);
+    if (ret < 0) {
+        ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
         range[0] = 0;
         range[1] = len;
-
-        if (range[1] == 0)
-            return 0;
-
-        ret = ioctl(fd, BLKSECDISCARD, &range);
+        ret = ioctl(fd, BLKDISCARD, &range);
         if (ret < 0) {
-            ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
-            range[0] = 0;
-            range[1] = len;
-            ret = ioctl(fd, BLKDISCARD, &range);
-            if (ret < 0) {
-                ALOGE("Discard failed: %s\n", strerror(errno));
-                return -1;
-            } else {
-                ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
-                return 0;
-            }
-
+            ALOGE("Discard failed: %s\n", strerror(errno));
+            return -1;
+        } else {
+            ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
+            return 0;
         }
-
-        return ret;
     }
 
-    static jlong com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
-    {
-        ScopedUtfChars path(env, jpath);
-        int fd = open(path.c_str(), O_RDONLY);
+    return ret;
+}
 
-        if (fd < 0)
-            return 0;
+static jlong com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env,
+                                                                                  jclass,
+                                                                                  jstring jpath) {
+    ScopedUtfChars path(env, jpath);
+    int fd = open(path.c_str(), O_RDONLY);
 
-        const uint64_t size = get_block_device_size(fd);
+    if (fd < 0) return 0;
 
-        close(fd);
+    const uint64_t size = get_block_device_size(fd);
 
-        return size;
-    }
+    close(fd);
 
-    static int com_android_server_pdb_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
-        ScopedUtfChars path(env, jpath);
-        int fd = open(path.c_str(), O_WRONLY);
+    return size;
+}
 
-        if (fd < 0)
-            return 0;
+static int com_android_server_pdb_PersistentDataBlockService_wipe(JNIEnv *env, jclass,
+                                                                  jstring jpath) {
+    ScopedUtfChars path(env, jpath);
+    int fd = open(path.c_str(), O_WRONLY);
 
-        const int ret = wipe_block_device(fd);
+    if (fd < 0) return 0;
 
-        close(fd);
+    const int ret = wipe_block_device(fd);
 
-        return ret;
-    }
+    close(fd);
 
-    static const JNINativeMethod sMethods[] = {
-         /* name, signature, funcPtr */
-        {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize},
-        {"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_pdb_PersistentDataBlockService_wipe},
-    };
+    return ret;
+}
 
-    int register_android_server_pdb_PersistentDataBlockService(JNIEnv* env)
-    {
-        return jniRegisterNativeMethods(env, "com/android/server/pdb/PersistentDataBlockService",
-                                        sMethods, NELEM(sMethods));
-    }
+static const JNINativeMethod sMethods[] = {
+        /* name, signature, funcPtr */
+        {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J",
+         (void *)com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize},
+        {"nativeWipe", "(Ljava/lang/String;)I",
+         (void *)com_android_server_pdb_PersistentDataBlockService_wipe},
+};
+
+int register_android_server_pdb_PersistentDataBlockService(JNIEnv *env) {
+    return jniRegisterNativeMethods(env, "com/android/server/pdb/PersistentDataBlockService",
+                                    sMethods, NELEM(sMethods));
+}
 
 } /* namespace android */
\ No newline at end of file
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index 5adcfba..4b5f8a7 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -19,4 +19,7 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.midi-sources"],
     libs: ["services.core"],
+    static_libs: [
+        "aconfig_midi_flags_java_lib",
+    ],
 }
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index a8902fc..2f47cc7 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.midi;
 
+import static com.android.media.midi.flags.Flags.virtualUmp;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
@@ -1549,6 +1551,12 @@
                 return;
             }
 
+            if (!virtualUmp()) {
+                Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
+                        + ": virtual UMP flag not enabled");
+                return;
+            }
+
             Bundle properties = null;
             int numPorts = 0;
             boolean isPrivate = false;
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 952cfc4..cbedcaf 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
@@ -43,6 +43,7 @@
 
 import android.annotation.NonNull;
 import android.app.PropertyInvalidatedCache;
+import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.SuspendDialogInfo;
@@ -873,12 +874,20 @@
                 .setUid(packageSetting.getAppId())
                 .hideAsFinal());
 
-        ArchiveState archiveState = new ArchiveState(
-                List.of(new ArchiveState.ArchiveActivityInfo("title1", Path.of("/path1"),
-                                Path.of("/monochromePath1")),
-                        new ArchiveState.ArchiveActivityInfo("title2", Path.of("/path2"),
-                                Path.of("/monochromePath2"))),
-                "installerTitle");
+        ArchiveState archiveState =
+                new ArchiveState(
+                        List.of(
+                                new ArchiveState.ArchiveActivityInfo(
+                                        "title1",
+                                        new ComponentName("pkg1", "class1"),
+                                        Path.of("/path1"),
+                                        Path.of("/monochromePath1")),
+                                new ArchiveState.ArchiveActivityInfo(
+                                        "title2",
+                                        new ComponentName("pkg2", "class2"),
+                                        Path.of("/path2"),
+                                        Path.of("/monochromePath2"))),
+                        "installerTitle");
         packageSetting.modifyUserState(UserHandle.SYSTEM.getIdentifier()).setArchiveState(
                 archiveState);
         settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java
index 58ae740..87a297b 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.overlay.OverlayPaths;
@@ -192,8 +193,8 @@
         return new SuspendParams(dialogInfo, appExtras, launcherExtras);
     }
 
-    private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
-            String sValue, String dKey, double dValue) {
+    private static PersistableBundle createPersistableBundle(
+            String lKey, long lValue, String sKey, String sValue, String dKey, double dValue) {
         final PersistableBundle result = new PersistableBundle(3);
         if (lKey != null) {
             result.putLong("com.unit_test." + lKey, lValue);
@@ -320,6 +321,7 @@
             assertEquals(0L, state.getLastPackageUsageTimeInMills()[i]);
         }
     }
+
     private static void assertLastPackageUsageSet(
             PackageStateUnserialized state, int reason, long value) throws Exception {
         for (int i = state.getLastPackageUsageTimeInMills().length - 1; i >= 0; --i) {
@@ -330,6 +332,7 @@
             }
         }
     }
+
     @Test
     public void testPackageUseReasons() throws Exception {
         PackageSetting packageSetting = Mockito.mock(PackageSetting.class);
@@ -377,6 +380,7 @@
         assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
         assertFalse(testState.setOverlayPaths(null));
     }
+
     @Test
     public void testSharedLibOverlayPaths() {
         final PackageUserStateImpl testState = new PackageUserStateImpl();
@@ -401,8 +405,12 @@
     @Test
     public void archiveState() {
         PackageUserStateImpl packageUserState = new PackageUserStateImpl();
-        ArchiveState.ArchiveActivityInfo archiveActivityInfo = new ArchiveState.ArchiveActivityInfo(
-                "appTitle", Path.of("/path1"), Path.of("/path2"));
+        ArchiveState.ArchiveActivityInfo archiveActivityInfo =
+                new ArchiveState.ArchiveActivityInfo(
+                        "appTitle",
+                        new ComponentName("pkg", "class"),
+                        Path.of("/path1"),
+                        Path.of("/path2"));
         ArchiveState archiveState = new ArchiveState(List.of(archiveActivityInfo),
                 "installerTitle");
         packageUserState.setArchiveState(archiveState);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index d021f1d..16d72e4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -117,7 +117,6 @@
 import com.android.server.display.notifications.DisplayNotificationManager;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.lights.LightsManager;
-import com.android.server.pm.UserManagerInternal;
 import com.android.server.sensors.SensorManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -312,7 +311,6 @@
     @Mock SensorManager mSensorManager;
     @Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
     @Mock PackageManagerInternal mMockPackageManagerInternal;
-    @Mock UserManagerInternal mMockUserManagerInternal;
 
 
     @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -336,8 +334,6 @@
                 VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
-        LocalServices.removeServiceForTest(UserManagerInternal.class);
-        LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
         // TODO: b/287945043
         mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mResources = Mockito.spy(mContext.getResources());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index c4f72b3..6a95d5c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -102,6 +102,9 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.testutils.FakeDeviceConfigInterface;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -121,26 +124,28 @@
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 @SmallTest
 @RunWith(JUnitParamsRunner.class)
 public class DisplayModeDirectorTest {
     public static Collection<Object[]> getAppRequestedSizeTestCases() {
         var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
-                {DEFAULT_MODE_75.getModeId(), Float.POSITIVE_INFINITY,
-                        DEFAULT_MODE_75.getRefreshRate(), Map.of()},
-                {APP_MODE_HIGH_90.getModeId(), Float.POSITIVE_INFINITY,
-                        APP_MODE_HIGH_90.getRefreshRate(),
-                        Map.of(
+                {/*expectedBaseModeId*/ DEFAULT_MODE_75.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ Float.POSITIVE_INFINITY,
+                        /*expectedAppRequestedRefreshRate*/ DEFAULT_MODE_75.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of()},
+                {/*expectedBaseModeId*/ APP_MODE_HIGH_90.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ Float.POSITIVE_INFINITY,
+                        /*expectedAppRequestedRefreshRate*/ APP_MODE_HIGH_90.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of(
                                 Vote.PRIORITY_APP_REQUEST_SIZE,
                                 Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
                                         APP_MODE_HIGH_90.getPhysicalHeight()),
                                 Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
                                 Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()))},
-                {LIMIT_MODE_70.getModeId(), Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
-                        Map.of(
+                {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ Float.POSITIVE_INFINITY,
+                        /*expectedAppRequestedRefreshRate*/ Float.POSITIVE_INFINITY,
+                        /*votesWithPriorities*/ Map.of(
                                 Vote.PRIORITY_APP_REQUEST_SIZE,
                                 Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
                                         APP_MODE_HIGH_90.getPhysicalHeight()),
@@ -149,9 +154,10 @@
                                 Vote.PRIORITY_LOW_POWER_MODE,
                                 Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
                                         LIMIT_MODE_70.getPhysicalHeight()))},
-                {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
-                        LIMIT_MODE_70.getRefreshRate(),
-                        Map.of(
+                {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+                        /*expectedAppRequestedRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of(
                                 Vote.PRIORITY_APP_REQUEST_SIZE,
                                 Vote.forSize(APP_MODE_65.getPhysicalWidth(),
                                         APP_MODE_65.getPhysicalHeight()),
@@ -160,9 +166,10 @@
                                 Vote.PRIORITY_LOW_POWER_MODE,
                                 Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
                                         LIMIT_MODE_70.getPhysicalHeight()))},
-                {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
-                        LIMIT_MODE_70.getRefreshRate(),
-                        Map.of(
+                {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+                        /*expectedAppRequestedRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of(
                                 Vote.PRIORITY_APP_REQUEST_SIZE,
                                 Vote.forSize(APP_MODE_65.getPhysicalWidth(),
                                         APP_MODE_65.getPhysicalHeight()),
@@ -173,10 +180,12 @@
                                     0, 0,
                                     LIMIT_MODE_70.getPhysicalWidth(),
                                     LIMIT_MODE_70.getPhysicalHeight(),
-                                    0, Float.POSITIVE_INFINITY)), false},
-                {APP_MODE_65.getModeId(), APP_MODE_65.getRefreshRate(),
-                        APP_MODE_65.getRefreshRate(),
-                        Map.of(
+                                    0, Float.POSITIVE_INFINITY)),
+                        /*displayResolutionRangeVotingEnabled*/ false},
+                {/*expectedBaseModeId*/ APP_MODE_65.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ APP_MODE_65.getRefreshRate(),
+                        /*expectedAppRequestedRefreshRate*/ APP_MODE_65.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of(
                                 Vote.PRIORITY_APP_REQUEST_SIZE,
                                 Vote.forSize(APP_MODE_65.getPhysicalWidth(),
                                         APP_MODE_65.getPhysicalHeight()),
@@ -187,7 +196,40 @@
                                     0, 0,
                                     LIMIT_MODE_70.getPhysicalWidth(),
                                     LIMIT_MODE_70.getPhysicalHeight(),
-                                    0, Float.POSITIVE_INFINITY)), true}});
+                                    0, Float.POSITIVE_INFINITY)),
+                        /*displayResolutionRangeVotingEnabled*/ true},
+                {/*expectedBaseModeId*/ DEFAULT_MODE_75.getModeId(),
+                        /*expectedPhysicalRefreshRate*/ APP_MODE_65.getRefreshRate(),
+                        /*expectedAppRequestedRefreshRate*/ APP_MODE_HIGH_90.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of(
+                                Vote.PRIORITY_APP_REQUEST_SIZE,
+                                Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+                                        APP_MODE_HIGH_90.getPhysicalHeight()),
+                                Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+                                Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
+                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.forSizeAndPhysicalRefreshRatesRange(
+                                    0, 0,
+                                    LIMIT_MODE_70.getPhysicalWidth(),
+                                    LIMIT_MODE_70.getPhysicalHeight(),
+                                    0, APP_MODE_65.getRefreshRate())),
+                        /*displayResolutionRangeVotingEnabled*/ false},
+                {/*expectedBaseModeId*/ DEFAULT_MODE_60.getModeId(), // Resolution == APP_MODE_65
+                        /*expectedPhysicalRefreshRate*/ APP_MODE_65.getRefreshRate(),
+                        /*expectedAppRequestedRefreshRate*/ APP_MODE_65.getRefreshRate(),
+                        /*votesWithPriorities*/ Map.of(
+                                Vote.PRIORITY_APP_REQUEST_SIZE,
+                                Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+                                        APP_MODE_HIGH_90.getPhysicalHeight()),
+                                Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+                                Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
+                                Vote.PRIORITY_LOW_POWER_MODE,
+                                Vote.forSizeAndPhysicalRefreshRatesRange(
+                                    0, 0,
+                                    LIMIT_MODE_70.getPhysicalWidth(),
+                                    LIMIT_MODE_70.getPhysicalHeight(),
+                                    0, APP_MODE_65.getRefreshRate())),
+                        /*displayResolutionRangeVotingEnabled*/ true}});
 
         final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2);
 
@@ -218,6 +260,8 @@
     private static final boolean DEBUG = false;
     private static final float FLOAT_TOLERANCE = 0.01f;
 
+    private static final Display.Mode DEFAULT_MODE_60 = new Display.Mode(
+            /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60);
     private static final Display.Mode APP_MODE_65 = new Display.Mode(
             /*modeId=*/65, /*width=*/1900, /*height=*/1900, 65);
     private static final Display.Mode LIMIT_MODE_70 = new Display.Mode(
@@ -227,8 +271,7 @@
     private static final Display.Mode APP_MODE_HIGH_90 = new Display.Mode(
             /*modeId=*/90, /*width=*/3000, /*height=*/3000, 90);
     private static final Display.Mode[] TEST_MODES = new Display.Mode[] {
-        new Display.Mode(
-            /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60),
+        DEFAULT_MODE_60,
         APP_MODE_65,
         LIMIT_MODE_70,
         DEFAULT_MODE_75,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 410ae35..367e14b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -228,7 +228,7 @@
         LocalServices.removeServiceForTest(AlarmManagerInternal.class);
         LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
-        doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
+        doNothing().when(mPackageManagerInt).notifyComponentUsed(any(), anyInt(), any(), any());
         doAnswer((invocation) -> {
             return getUidForPackage(invocation.getArgument(0));
         }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
@@ -1014,8 +1014,9 @@
                     eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
 
             // Confirm that we unstopped manifest receivers
-            verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(
-                    eq(receiverApp.info.packageName), eq(false), eq(UserHandle.USER_SYSTEM));
+            verify(mAms.mPackageManagerInt, atLeastOnce()).notifyComponentUsed(
+                    eq(receiverApp.info.packageName), eq(UserHandle.USER_SYSTEM),
+                    eq(callerApp.info.packageName), any());
         }
 
         // Confirm that we've reported expected usage events
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
index 1ce79a5..05ac5b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.display;
 
-import static android.os.Process.INVALID_UID;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
@@ -35,7 +33,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -55,7 +53,10 @@
     @Mock
     private PackageManagerInternal mMockPackageManagerInternal;
     @Mock
-    private UserManagerInternal mMockUserManagerInternal;
+    private PackageStateInternal mMockPkgStateA;
+    @Mock
+    private PackageStateInternal mMockPkgStateB;
+
 
     private SmallAreaDetectionController mSmallAreaDetectionController;
 
@@ -64,29 +65,18 @@
     private static final String PKG_NOT_INSTALLED = "com.not.installed";
     private static final float THRESHOLD_A = 0.05f;
     private static final float THRESHOLD_B = 0.07f;
-    private static final int USER_1 = 110;
-    private static final int USER_2 = 111;
-    private static final int UID_A_1 = 11011111;
-    private static final int UID_A_2 = 11111111;
-    private static final int UID_B_1 = 11022222;
-    private static final int UID_B_2 = 11122222;
+    private static final int APP_ID_A = 11111;
+    private static final int APP_ID_B = 22222;
 
     @Before
     public void setup() {
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
-        LocalServices.removeServiceForTest(UserManagerInternal.class);
-        LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
 
-        when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[]{USER_1, USER_2});
-        when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_1)).thenReturn(UID_A_1);
-        when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_2)).thenReturn(UID_A_2);
-        when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_1)).thenReturn(UID_B_1);
-        when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_2)).thenReturn(UID_B_2);
-        when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_1)).thenReturn(
-                INVALID_UID);
-        when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_2)).thenReturn(
-                INVALID_UID);
+        when(mMockPackageManagerInternal.getPackageStateInternal(PKG_A)).thenReturn(mMockPkgStateA);
+        when(mMockPackageManagerInternal.getPackageStateInternal(PKG_B)).thenReturn(mMockPkgStateB);
+        when(mMockPkgStateA.getAppId()).thenReturn(APP_ID_A);
+        when(mMockPkgStateB.getAppId()).thenReturn(APP_ID_B);
 
         mSmallAreaDetectionController = spy(new SmallAreaDetectionController(
                 new ContextWrapper(ApplicationProvider.getApplicationContext()),
@@ -99,9 +89,9 @@
         final String property = PKG_A + ":" + THRESHOLD_A + "," + PKG_B + ":" + THRESHOLD_B;
         mSmallAreaDetectionController.updateAllowlist(property);
 
-        final int[] resultUidArray = {UID_A_1, UID_B_1, UID_A_2, UID_B_2};
-        final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B, THRESHOLD_A, THRESHOLD_B};
-        verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+        final int[] resultAppIdArray = {APP_ID_A, APP_ID_B};
+        final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B};
+        verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
                 eq(resultThresholdArray));
     }
 
@@ -110,9 +100,9 @@
         final String property = PKG_A + "," + PKG_B + ":" + THRESHOLD_B;
         mSmallAreaDetectionController.updateAllowlist(property);
 
-        final int[] resultUidArray = {UID_B_1, UID_B_2};
-        final float[] resultThresholdArray = {THRESHOLD_B, THRESHOLD_B};
-        verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+        final int[] resultAppIdArray = {APP_ID_B};
+        final float[] resultThresholdArray = {THRESHOLD_B};
+        verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
                 eq(resultThresholdArray));
     }
 
@@ -122,9 +112,9 @@
                 PKG_A + ":" + THRESHOLD_A + "," + PKG_NOT_INSTALLED + ":" + THRESHOLD_B;
         mSmallAreaDetectionController.updateAllowlist(property);
 
-        final int[] resultUidArray = {UID_A_1, UID_A_2};
-        final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_A};
-        verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+        final int[] resultAppIdArray = {APP_ID_A};
+        final float[] resultThresholdArray = {THRESHOLD_A};
+        verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
                 eq(resultThresholdArray));
     }
 
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 eb50556..610ea90 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -427,6 +428,7 @@
         for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
             ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo(
                     mainActivity.getLabel().toString(),
+                    mainActivity.getComponentName(),
                     ICON_PATH, null);
             activityInfos.add(activityInfo);
         }
@@ -437,9 +439,11 @@
         ActivityInfo activityInfo = mock(ActivityInfo.class);
         LauncherActivityInfo activity1 = mock(LauncherActivityInfo.class);
         when(activity1.getLabel()).thenReturn("activity1");
+        when(activity1.getComponentName()).thenReturn(new ComponentName("pkg1", "class1"));
         when(activity1.getActivityInfo()).thenReturn(activityInfo);
         LauncherActivityInfo activity2 = mock(LauncherActivityInfo.class);
         when(activity2.getLabel()).thenReturn("activity2");
+        when(activity2.getComponentName()).thenReturn(new ComponentName("pkg2", "class2"));
         when(activity2.getActivityInfo()).thenReturn(activityInfo);
         return List.of(activity1, activity2);
     }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index e8cbcf9..a3d415e 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -337,11 +337,7 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
 
-        mSetFlagsRule.disableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
-        mSetFlagsRule.disableFlags(Flags.FLAG_DYNAMIC_POLICY);
-        mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
-        mSetFlagsRule.disableFlags(Flags.FLAG_VDM_CUSTOM_HOME);
-        mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_NATIVE_VDM);
+        mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
 
         doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
         doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index 12d6161..5cc84b1 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -59,6 +59,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -103,6 +104,10 @@
 
     private boolean mDevCfgEnableContentProtectionReceiver;
 
+    private List<List<String>> mDevCfgContentProtectionRequiredGroups = List.of(List.of("a"));
+
+    private List<List<String>> mDevCfgContentProtectionOptionalGroups = Collections.emptyList();
+
     private int mContentProtectionBlocklistManagersCreated;
 
     private int mContentProtectionServiceInfosCreated;
@@ -374,7 +379,21 @@
     }
 
     @Test
-    public void isContentProtectionReceiverEnabled_withoutManagers() {
+    public void isContentProtectionReceiverEnabled_true() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
+        when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
+        mDevCfgEnableContentProtectionReceiver = true;
+        mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+        boolean actual =
+                mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+                        USER_ID, PACKAGE_NAME);
+
+        assertThat(actual).isTrue();
+    }
+
+    @Test
+    public void isContentProtectionReceiverEnabled_false_withoutManagers() {
         boolean actual =
                 mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
                         USER_ID, PACKAGE_NAME);
@@ -385,7 +404,7 @@
     }
 
     @Test
-    public void isContentProtectionReceiverEnabled_disabledWithFlag() {
+    public void isContentProtectionReceiverEnabled_false_disabledWithFlag() {
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
         mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = false;
@@ -400,6 +419,22 @@
     }
 
     @Test
+    public void isContentProtectionReceiverEnabled_false_emptyGroups() {
+        mDevCfgEnableContentProtectionReceiver = true;
+        mDevCfgContentProtectionRequiredGroups = Collections.emptyList();
+        mDevCfgContentProtectionOptionalGroups = Collections.emptyList();
+        mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+        boolean actual =
+                mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+                        USER_ID, PACKAGE_NAME);
+
+        assertThat(actual).isFalse();
+        verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
+        verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+    }
+
+    @Test
     public void onLoginDetected_disabledAfterConstructor() {
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -525,6 +560,10 @@
             super(sContext);
             this.mDevCfgEnableContentProtectionReceiver =
                     ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver;
+            this.mDevCfgContentProtectionRequiredGroups =
+                    ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionRequiredGroups;
+            this.mDevCfgContentProtectionOptionalGroups =
+                    ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionOptionalGroups;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java
new file mode 100644
index 0000000..07cdf4d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java
@@ -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.server.media.projection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for the {@link MediaProjectionSessionIdGenerator} class.
+ *
+ * <p>Build/Install/Run: atest FrameworksServicesTests:MediaProjectionSessionIdGeneratorTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class MediaProjectionSessionIdGeneratorTest {
+
+    private static final String TEST_PREFS_FILE = "media-projection-session-id-test";
+
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+    private final File mSharedPreferencesFile = new File(mContext.getCacheDir(), TEST_PREFS_FILE);
+    private final SharedPreferences mSharedPreferences = createSharePreferences();
+    private final MediaProjectionSessionIdGenerator mGenerator =
+            createGenerator(mSharedPreferences);
+
+    @Before
+    public void setUp() {
+        mSharedPreferences.edit().clear().commit();
+    }
+
+    @After
+    public void tearDown() {
+        mSharedPreferences.edit().clear().commit();
+        mSharedPreferencesFile.delete();
+    }
+
+    @Test
+    public void getCurrentSessionId_byDefault_returns0() {
+        assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+    }
+
+    @Test
+    public void getCurrentSessionId_multipleTimes_returnsSameValue() {
+        assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+        assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+        assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+    }
+
+    @Test
+    public void createAndGetNewSessionId_returnsIncrementedId() {
+        int previousValue = mGenerator.getCurrentSessionId();
+
+        int newValue = mGenerator.createAndGetNewSessionId();
+
+        assertThat(newValue).isEqualTo(previousValue + 1);
+    }
+
+    @Test
+    public void createAndGetNewSessionId_persistsNewValue() {
+        int newValue = mGenerator.createAndGetNewSessionId();
+
+        MediaProjectionSessionIdGenerator newInstance = createGenerator(createSharePreferences());
+
+        assertThat(newInstance.getCurrentSessionId()).isEqualTo(newValue);
+    }
+
+    private SharedPreferences createSharePreferences() {
+        return mContext.getSharedPreferences(mSharedPreferencesFile, Context.MODE_PRIVATE);
+    }
+
+    private MediaProjectionSessionIdGenerator createGenerator(SharedPreferences sharedPreferences) {
+        return new MediaProjectionSessionIdGenerator(sharedPreferences);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 8e7ba70..dd7dec0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -130,12 +130,22 @@
         // verify if back animation would start.
         assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
 
-        // reset drawing status
+        // reset drawing status to test translucent activity
         backNavigationInfo.onBackNavigationFinished(false);
         mBackNavigationController.clearBackAnimations();
-        topTask.forAllWindows(w -> {
-            makeWindowVisibleAndDrawn(w);
-        }, true);
+        final ActivityRecord topActivity = topTask.getTopMostActivity();
+        makeWindowVisibleAndDrawn(topActivity.findMainWindow());
+        // simulate translucent
+        topActivity.setOccludesParent(false);
+        backNavigationInfo = startBackNavigation();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
+
+        // reset drawing status to test keyguard occludes
+        topActivity.setOccludesParent(true);
+        backNavigationInfo.onBackNavigationFinished(false);
+        mBackNavigationController.clearBackAnimations();
+        makeWindowVisibleAndDrawn(topActivity.findMainWindow());
         setupKeyguardOccluded();
         backNavigationInfo = startBackNavigation();
         assertThat(typeToString(backNavigationInfo.getType()))
@@ -201,9 +211,7 @@
         // reset drawing status
         backNavigationInfo.onBackNavigationFinished(false);
         mBackNavigationController.clearBackAnimations();
-        testCase.recordFront.forAllWindows(w -> {
-            makeWindowVisibleAndDrawn(w);
-        }, true);
+        makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
         setupKeyguardOccluded();
         backNavigationInfo = startBackNavigation();
         assertThat(typeToString(backNavigationInfo.getType()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 2bf1385..6235b3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -26,7 +26,9 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
@@ -1655,6 +1657,127 @@
         assertEquals(frontMostTaskFragment, tf0);
     }
 
+    @Test
+    public void testApplyTransaction_reorderToBottomOfTask() {
+        mController.unregisterOrganizer(mIOrganizer);
+        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        final Task task = createTask(mDisplayContent);
+        // Create a non-embedded Activity at the bottom.
+        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .build();
+        final TaskFragment tf0 = createTaskFragment(task);
+        final TaskFragment tf1 = createTaskFragment(task);
+        // Create a non-embedded Activity at the top.
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .build();
+
+        // Ensure correct order of the children before the operation
+        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+        // Reorder TaskFragment to bottom
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                OP_TYPE_REORDER_TO_BOTTOM_OF_TASK).build();
+        mTransaction.addTaskFragmentOperation(tf1.getFragmentToken(), operation);
+        assertApplyTransactionAllowed(mTransaction);
+
+        // Ensure correct order of the children after the operation
+        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+        assertEquals(tf0, task.getChildAt(2).asTaskFragment());
+        assertEquals(bottomActivity, task.getChildAt(1).asActivityRecord());
+        assertEquals(tf1, task.getChildAt(0).asTaskFragment());
+    }
+
+    @Test
+    public void testApplyTransaction_reorderToTopOfTask() {
+        mController.unregisterOrganizer(mIOrganizer);
+        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        final Task task = createTask(mDisplayContent);
+        // Create a non-embedded Activity at the bottom.
+        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .build();
+        final TaskFragment tf0 = createTaskFragment(task);
+        final TaskFragment tf1 = createTaskFragment(task);
+        // Create a non-embedded Activity at the top.
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .build();
+
+        // Ensure correct order of the children before the operation
+        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+        // Reorder TaskFragment to top
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                OP_TYPE_REORDER_TO_TOP_OF_TASK).build();
+        mTransaction.addTaskFragmentOperation(tf0.getFragmentToken(), operation);
+        assertApplyTransactionAllowed(mTransaction);
+
+        // Ensure correct order of the children after the operation
+        assertEquals(tf0, task.getChildAt(3).asTaskFragment());
+        assertEquals(topActivity, task.getChildAt(2).asActivityRecord());
+        assertEquals(tf1, task.getChildAt(1).asTaskFragment());
+        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+    }
+
+    @Test
+    public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
+        testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+                OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
+    }
+
+    @Test
+    public void testApplyTransaction_reorderToTopOfTask_failsIfNotSystemOrganizer() {
+        testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+                OP_TYPE_REORDER_TO_TOP_OF_TASK);
+    }
+
+    private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+            @TaskFragmentOperation.OperationType int opType) {
+        final Task task = createTask(mDisplayContent);
+        // Create a non-embedded Activity at the bottom.
+        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .build();
+        final TaskFragment tf0 = createTaskFragment(task);
+        final TaskFragment tf1 = createTaskFragment(task);
+        // Create a non-embedded Activity at the top.
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .build();
+
+        // Ensure correct order of the children before the operation
+        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+        // Apply reorder transaction, which is expected to fail for non-system organizer.
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                opType).build();
+        mTransaction
+                .addTaskFragmentOperation(tf0.getFragmentToken(), operation)
+                .setErrorCallbackToken(mErrorToken);
+        assertApplyTransactionAllowed(mTransaction);
+        // The pending event will be dispatched on the handler (from requestTraversal).
+        waitHandlerIdle(mWm.mAnimationHandler);
+
+        assertTaskFragmentErrorTransaction(opType, SecurityException.class);
+
+        // Ensure no change to the order of the children after the operation
+        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+    }
+
     /**
      * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
      * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the
@@ -1782,6 +1905,19 @@
         assertEquals(activityToken, change.getActivityToken());
     }
 
+    /** Setups an embedded TaskFragment. */
+    private TaskFragment createTaskFragment(Task task) {
+        final IBinder token = new Binder();
+        TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .setFragmentToken(token)
+                .setOrganizer(mOrganizer)
+                .createActivityCount(1)
+                .build();
+        mWindowOrganizerController.mLaunchTaskFragments.put(token, taskFragment);
+        return taskFragment;
+    }
+
     /** Setups an embedded TaskFragment in a PIP Task. */
     private void setupTaskFragmentInPip() {
         mTaskFragment = new TaskFragmentBuilder(mAtm)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index e86fc36..eaeb804 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -100,6 +100,9 @@
 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
 import com.android.internal.os.IResultReceiver;
 import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerService.WindowContainerInfo;
+
+import com.google.common.truth.Expect;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -125,6 +128,9 @@
             InstrumentationRegistry.getInstrumentation().getUiAutomation(),
             ADD_TRUSTED_DISPLAY);
 
+    @Rule
+    public Expect mExpect = Expect.create();
+
     @Test
     public void testIsRequestedOrientationMapped() {
         mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ true,
@@ -674,64 +680,68 @@
 
     @Test
     public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() {
-        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(null);
-        assertThat(wct).isNull();
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(null);
+        assertThat(wci).isNull();
     }
 
     @Test
     public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() {
         Binder cookie = new Binder("test cookie");
-        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
-        assertThat(wct).isNull();
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+        assertThat(wci).isNull();
 
         final ActivityRecord testActivity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
                 .build();
 
-        wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
-        assertThat(wct).isNull();
+        wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+        assertThat(wci).isNull();
     }
 
     @Test
     public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() {
         final Binder cookie = new Binder("ginger cookie");
         final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
-        setupActivityWithLaunchCookie(cookie, launchRootTask);
+        final int uid = 123;
+        setupActivityWithLaunchCookie(cookie, launchRootTask, uid);
 
-        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
-        assertThat(wct).isEqualTo(launchRootTask);
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+        mExpect.that(wci.getToken()).isEqualTo(launchRootTask);
+        mExpect.that(wci.getUid()).isEqualTo(uid);
     }
 
     @Test
     public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() {
         final Binder cookie1 = new Binder("ginger cookie");
         final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class);
-        setupActivityWithLaunchCookie(cookie1, launchRootTask1);
+        final int uid1 = 123;
+        setupActivityWithLaunchCookie(cookie1, launchRootTask1, uid1);
 
         setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
-                mock(WindowContainerToken.class));
+                mock(WindowContainerToken.class), /* uid= */ 456);
 
         setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
-                mock(WindowContainerToken.class));
+                mock(WindowContainerToken.class), /* uid= */ 789);
 
-        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie1);
-        assertThat(wct).isEqualTo(launchRootTask1);
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie1);
+        mExpect.that(wci.getToken()).isEqualTo(launchRootTask1);
+        mExpect.that(wci.getUid()).isEqualTo(uid1);
     }
 
     @Test
     public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() {
         setupActivityWithLaunchCookie(new Binder("ginger cookie"),
-                mock(WindowContainerToken.class));
+                mock(WindowContainerToken.class), /* uid= */ 123);
 
         setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
-                mock(WindowContainerToken.class));
+                mock(WindowContainerToken.class), /* uid= */ 456);
 
         setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
-                mock(WindowContainerToken.class));
+                mock(WindowContainerToken.class), /* uid= */ 789);
 
-        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(
                 new Binder("some other cookie"));
-        assertThat(wct).isNull();
+        assertThat(wci).isNull();
     }
 
     @Test
@@ -778,17 +788,18 @@
     }
 
     @Test
-    public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken() {
+    public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerInfo() {
         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
         Task task = createTask(mDefaultDisplay);
         ActivityRecord activityRecord = createActivityRecord(task);
-        ContentRecordingSession session = ContentRecordingSession.createTaskSession(
-                activityRecord.mLaunchCookie);
+        ContentRecordingSession session =
+                ContentRecordingSession.createTaskSession(activityRecord.mLaunchCookie);
 
         wmInternal.setContentRecordingSession(session);
 
-        assertThat(session.getTokenToRecord()).isEqualTo(
-                task.mRemoteToken.toWindowContainerToken().asBinder());
+        mExpect.that(session.getTokenToRecord())
+                .isEqualTo(task.mRemoteToken.toWindowContainerToken().asBinder());
+        mExpect.that(session.getTargetUid()).isEqualTo(activityRecord.getUid());
     }
 
     @Test
@@ -1010,12 +1021,12 @@
         }
     }
 
-    private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
+    private void setupActivityWithLaunchCookie(
+            IBinder launchCookie, WindowContainerToken wct, int uid) {
         final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
         when(remoteToken.toWindowContainerToken()).thenReturn(wct);
-        final ActivityRecord testActivity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .build();
+        final ActivityRecord testActivity =
+                new ActivityBuilder(mAtm).setCreateTask(true).setUid(uid).build();
         testActivity.mLaunchCookie = launchCookie;
         testActivity.getTask().mRemoteToken = remoteToken;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 7168670..0b77fd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -40,6 +40,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 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.wm.testing.Assert.assertThrows;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -58,6 +59,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
@@ -77,11 +79,13 @@
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.WindowInsets;
+import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskOrganizer;
 import android.window.IWindowContainerTransactionCallback;
 import android.window.StartingWindowInfo;
 import android.window.StartingWindowRemovalInfo;
 import android.window.TaskAppearedInfo;
+import android.window.TaskFragmentOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -579,6 +583,87 @@
     }
 
     @Test
+    public void testTaskFragmentHiddenAndFocusableChanges() {
+        removeGlobalMinSizeRestriction();
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+        final TaskFragmentOrganizer organizer =
+                createTaskFragmentOrganizer(t, true /* isSystemOrganizer */);
+
+        final IBinder token = new Binder();
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(rootTask)
+                .setFragmentToken(token)
+                .setOrganizer(organizer)
+                .createActivityCount(1)
+                .build();
+
+        // Should be visible and focusable initially.
+        assertTrue(rootTask.shouldBeVisible(null));
+        assertTrue(taskFragment.shouldBeVisible(null));
+        assertTrue(taskFragment.isFocusable());
+        assertTrue(taskFragment.isTopActivityFocusable());
+
+        // Apply transaction to the TaskFragment hidden and not focusable.
+        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+        mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+                t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+                false /* shouldApplyIndependently */);
+
+        // Should be not visible and not focusable after the transaction.
+        assertFalse(taskFragment.shouldBeVisible(null));
+        assertFalse(taskFragment.isFocusable());
+        assertFalse(taskFragment.isTopActivityFocusable());
+
+        // Apply transaction to the TaskFragment not hidden and focusable.
+        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+        mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+                t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+                false /* shouldApplyIndependently */);
+
+        // Should be visible and focusable after the transaction.
+        assertTrue(taskFragment.shouldBeVisible(null));
+        assertTrue(taskFragment.isFocusable());
+        assertTrue(taskFragment.isTopActivityFocusable());
+    }
+
+    @Test
+    public void testTaskFragmentHiddenAndFocusableChanges_throwsWhenNotSystemOrganizer() {
+        removeGlobalMinSizeRestriction();
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+        final TaskFragmentOrganizer organizer =
+                createTaskFragmentOrganizer(t, false /* isSystemOrganizer */);
+
+        final IBinder token = new Binder();
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(rootTask)
+                .setFragmentToken(token)
+                .setOrganizer(organizer)
+                .createActivityCount(1)
+                .build();
+
+        assertTrue(rootTask.shouldBeVisible(null));
+        assertTrue(taskFragment.shouldBeVisible(null));
+
+        t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+        t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+
+        // Non-system organizers are not allow to update the hidden and focusable states.
+        assertThrows(SecurityException.class, () ->
+                mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+                        t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+                        false /* shouldApplyIndependently */)
+        );
+    }
+
+    @Test
     public void testContainerTranslucentChanges() {
         removeGlobalMinSizeRestriction();
         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
@@ -1600,4 +1685,20 @@
             assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
         }
     }
+
+    @NonNull
+    private TaskFragmentOrganizer createTaskFragmentOrganizer(
+            @NonNull WindowContainerTransaction t, boolean isSystemOrganizer) {
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final ITaskFragmentOrganizer organizerInterface =
+                ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+        mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
+                .registerOrganizerInternal(
+                        ITaskFragmentOrganizer.Stub.asInterface(
+                                organizer.getOrganizerToken().asBinder()),
+                        isSystemOrganizer);
+        t.setTaskFragmentOrganizer(organizerInterface);
+
+        return organizer;
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2e6278d..55b5d11 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2127,15 +2127,13 @@
         }
 
         private boolean canReportUsageStats() {
-            final boolean isSystem = isCallingUidSystem();
-            if (!Flags.reportUsageStatsPermission()) {
-                // If the flag is disabled, do no check for the new permission and instead return
-                // true only if the calling uid is system since System UID can always report stats.
-                return isSystem;
+            if (isCallingUidSystem()) {
+                // System UID can always report UsageStats
+                return true;
             }
-            return isSystem
-                    || getContext().checkCallingPermission(Manifest.permission.REPORT_USAGE_STATS)
-                        == PackageManager.PERMISSION_GRANTED;
+
+            return getContext().checkCallingPermission(Manifest.permission.REPORT_USAGE_STATS)
+                    == PackageManager.PERMISSION_GRANTED;
         }
 
         private boolean hasObserverPermission() {
@@ -2627,9 +2625,12 @@
                 return;
             }
 
-            if (!canReportUsageStats()) {
-                throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
-                        + " permission are allowed to call reportChooserSelection");
+            if (Flags.reportUsageStatsPermission()) {
+                if (!canReportUsageStats()) {
+                    throw new SecurityException(
+                        "Only the system or holders of the REPORT_USAGE_STATS"
+                            + " permission are allowed to call reportChooserSelection");
+                }
             }
 
             // Verify if this package exists before reporting an event for it.
@@ -2649,9 +2650,17 @@
         @Override
         public void reportUserInteraction(String packageName, int userId) {
             Objects.requireNonNull(packageName);
-            if (!canReportUsageStats()) {
-                throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
-                        + " permission are allowed to call reportUserInteraction");
+            if (Flags.reportUsageStatsPermission()) {
+                if (!canReportUsageStats()) {
+                    throw new SecurityException(
+                        "Only the system or holders of the REPORT_USAGE_STATS"
+                            + " permission are allowed to call reportUserInteraction");
+                }
+            } else {
+                if (!isCallingUidSystem()) {
+                    throw new SecurityException("Only system is allowed to call"
+                            + " reportUserInteraction");
+                }
             }
 
             final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 138e575..1c689d0 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -58,6 +58,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.PermissionEnforcer;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
@@ -67,6 +68,7 @@
 import android.os.ShellCallback;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.permission.flags.Flags;
 import android.provider.Settings;
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
 import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
@@ -1286,6 +1288,17 @@
             }
         }
 
+        // Enforce permissions that are flag controlled. The flag value decides if the permission
+        // should be enforced.
+        private void initAndVerifyDetector_enforcePermissionWithFlags() {
+            PermissionEnforcer enforcer = mContext.getSystemService(PermissionEnforcer.class);
+            if (Flags.voiceActivationPermissionApis()) {
+                enforcer.enforcePermission(
+                        android.Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO,
+                        getCallingPid(), getCallingUid());
+            }
+        }
+
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
         @Override
         public void initAndVerifyDetector(
@@ -1295,7 +1308,13 @@
                 @NonNull IBinder token,
                 IHotwordRecognitionStatusCallback callback,
                 int detectorType) {
+            // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+            // {@link #initAndVerifyDetector(Identity,  PersistableBundle, ShareMemory, IBinder,
+            // IHotwordRecognitionStatusCallback, int)}
+            // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully
+            // launched.
             super.initAndVerifyDetector_enforcePermission();
+            initAndVerifyDetector_enforcePermissionWithFlags();
 
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 971fc78..e20e4d2 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -202,6 +202,24 @@
                     && mConditionalBarringTimeSeconds == other.mConditionalBarringTimeSeconds;
         }
 
+        private static String barringTypeToString(@BarringType int barringType) {
+            return switch (barringType) {
+                case BARRING_TYPE_NONE -> "NONE";
+                case BARRING_TYPE_CONDITIONAL -> "CONDITIONAL";
+                case BARRING_TYPE_UNCONDITIONAL -> "UNCONDITIONAL";
+                case BARRING_TYPE_UNKNOWN -> "UNKNOWN";
+                default -> "UNKNOWN(" + barringType + ")";
+            };
+        }
+
+        @Override
+        public String toString() {
+            return "BarringServiceInfo {mBarringType=" + barringTypeToString(mBarringType)
+                    + ", mIsConditionallyBarred=" + mIsConditionallyBarred
+                    + ", mConditionalBarringFactor=" + mConditionalBarringFactor
+                    + ", mConditionalBarringTimeSeconds=" + mConditionalBarringTimeSeconds + "}";
+        }
+
         /** @hide */
         public BarringServiceInfo(Parcel p) {
             mBarringType = p.readInt();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 90fa69f..73220c3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15033,15 +15033,6 @@
     @TestApi
     public static final int HAL_SERVICE_IMS = 7;
 
-    /**
-     * HAL service type that supports the HAL APIs implementation of IRadioSatellite
-     * {@link RadioSatelliteProxy}
-     * @hide
-     */
-    @TestApi
-    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    public static final int HAL_SERVICE_SATELLITE = 8;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"HAL_SERVICE_"},
@@ -15054,7 +15045,6 @@
                     HAL_SERVICE_SIM,
                     HAL_SERVICE_VOICE,
                     HAL_SERVICE_IMS,
-                    HAL_SERVICE_SATELLITE
             })
     public @interface HalService {}
 
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 72e4389..a20f26c 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -120,31 +120,6 @@
     int BLOCKED_DUE_TO_CALL = 69;                   /* SMS is blocked due to call control */
     int RF_HARDWARE_ISSUE = 70;                     /* RF HW issue is detected */
     int NO_RF_CALIBRATION_INFO = 71;                /* No RF calibration in device */
-    int ENCODING_NOT_SUPPORTED = 72;                /* The encoding scheme is not supported by
-                                                       either the network or the MS. */
-    int FEATURE_NOT_SUPPORTED = 73;                 /* The requesting feature is not supported by
-                                                       the service provider. */
-    int INVALID_CONTACT = 74;                       /* The contact to be added is either not
-                                                       existing or not valid. */
-    int MODEM_INCOMPATIBLE = 75;                    /* The modem of the MS is not compatible with
-                                                       the service provider. */
-    int NETWORK_TIMEOUT = 76;                       /* Modem timeout to receive ACK or response from
-                                                       network after sending a request to it. */
-    int NO_SATELLITE_SIGNAL = 77;                   /* Modem fails to communicate with the satellite
-                                                       network since there is no satellite signal.*/
-    int NOT_SUFFICIENT_ACCOUNT_BALANCE = 78;        /* The request cannot be performed since the
-                                                       subscriber's account balance is not
-                                                       sufficient. */
-    int RADIO_TECHNOLOGY_NOT_SUPPORTED = 79;        /* The radio technology is not supported by the
-                                                       service provider. */
-    int SUBSCRIBER_NOT_AUTHORIZED = 80;             /* The subscription is not authorized to
-                                                       register with the service provider. */
-    int SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 81; /* While processing a request from the
-                                                       Framework the satellite modem detects
-                                                       terrestrial signal, aborts the request, and
-                                                       switches to the terrestrial network. */
-    int UNIDENTIFIED_SUBSCRIBER = 82;               /* The subscriber is not registered with the
-                                                       service provider */
 
     // Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
     // reveal particular replacement for Generic failure
@@ -568,22 +543,7 @@
     int RIL_REQUEST_IS_N1_MODE_ENABLED = 242;
     int RIL_REQUEST_SET_LOCATION_PRIVACY_SETTING = 243;
     int RIL_REQUEST_GET_LOCATION_PRIVACY_SETTING = 244;
-    int RIL_REQUEST_GET_SATELLITE_CAPABILITIES = 245;
-    int RIL_REQUEST_SET_SATELLITE_POWER = 246;
-    int RIL_REQUEST_GET_SATELLITE_POWER = 247;
-    int RIL_REQUEST_PROVISION_SATELLITE_SERVICE = 248;
-    int RIL_REQUEST_ADD_ALLOWED_SATELLITE_CONTACTS = 249;
-    int RIL_REQUEST_REMOVE_ALLOWED_SATELLITE_CONTACTS = 250;
-    int RIL_REQUEST_SEND_SATELLITE_MESSAGES = 251;
-    int RIL_REQUEST_GET_PENDING_SATELLITE_MESSAGES = 252;
-    int RIL_REQUEST_GET_SATELLITE_MODE = 253;
-    int RIL_REQUEST_SET_SATELLITE_INDICATION_FILTER = 254;
-    int RIL_REQUEST_START_SENDING_SATELLITE_POINTING_INFO = 255;
-    int RIL_REQUEST_STOP_SENDING_SATELLITE_POINTING_INFO = 256;
-    int RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE = 257;
-    int RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY = 258;
-    int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 259;
-    int RIL_REQUEST_SET_SATELLITE_PLMN = 260;
+    int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 245;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -645,13 +605,6 @@
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
     int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
-    int RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT = 1056;
-    int RIL_UNSOL_NEW_SATELLITE_MESSAGES = 1057;
-    int RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE = 1058;
-    int RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED = 1059;
-    int RIL_UNSOL_SATELLITE_MODE_CHANGED = 1060;
-    int RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED = 1061;
-    int RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED = 1062;
 
     /* The following unsols are not defined in RIL.h */
     int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index 359845d..47d6d23 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -36,7 +36,8 @@
 /**
  * Test launching a secondary Activity into Picture-In-Picture mode.
  *
- * Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom
+ * Setup: Start from a split A|B.
+ * Transition: B enters PIP, observe the window first goes fullscreen then shrink to the bottom
  * right corner on screen.
  *
  * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
@@ -63,7 +64,16 @@
         }
     }
 
-    /** Main and secondary activity start from a split each taking half of the screen. */
+    /**
+     * We expect the background layer to be visible during this transition.
+     */
+    @Presubmit
+    @Test
+    override fun backgroundLayerNeverVisible(): Unit {}
+
+    /**
+     * Main and secondary activity start from a split each taking half of the screen.
+     */
     @Presubmit
     @Test
     fun layersStartFromEqualSplit() {
@@ -109,7 +119,7 @@
                 .isVisible(TRANSITION_SNAPSHOT)
                 .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
                 .then()
-                .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+                .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT, isOptional = true)
         }
         flicker.assertLayersEnd {
             visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 0599c1d..226e2fad 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -7,9 +7,38 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+// Visibility only for ravenwood prototype uses.
+genrule_defaults {
+    name: "hoststubgen-for-prototype-only-genrule",
+    visibility: [
+        ":__subpackages__",
+        "//frameworks/base/ravenwood:__subpackages__",
+    ],
+}
+
+// Visibility only for ravenwood prototype uses.
+java_defaults {
+    name: "hoststubgen-for-prototype-only-java",
+    visibility: [
+        ":__subpackages__",
+        "//frameworks/base/ravenwood:__subpackages__",
+    ],
+}
+
+// Visibility only for ravenwood prototype uses.
+filegroup_defaults {
+    name: "hoststubgen-for-prototype-only-filegroup",
+    visibility: [
+        ":__subpackages__",
+        "//frameworks/base/ravenwood:__subpackages__",
+    ],
+}
+
 // This library contains the standard hoststubgen annotations.
+// This is only for the prototype. The productionized version is "ravenwood-annotations".
 java_library {
     name: "hoststubgen-annotations",
+    defaults: ["hoststubgen-for-prototype-only-java"],
     srcs: [
         "annotations-src/**/*.java",
     ],
@@ -18,7 +47,6 @@
     // Seems like we need it to avoid circular deps.
     // Copied it from "app-compat-annotations".
     sdk_version: "core_current",
-    visibility: ["//visibility:public"],
 }
 
 // This library contains helper classes used in the host side test environment at runtime.
@@ -55,12 +83,13 @@
 }
 
 // File that contains the standard command line argumetns to hoststubgen.
+// This is only for the prototype. The productionized version is "ravenwood-standard-options".
 filegroup {
     name: "hoststubgen-standard-options",
+    defaults: ["hoststubgen-for-prototype-only-filegroup"],
     srcs: [
         "hoststubgen-standard-options.txt",
     ],
-    visibility: ["//visibility:public"],
 }
 
 hoststubgen_common_options = "$(location hoststubgen) " +
@@ -93,7 +122,6 @@
         "hoststubgen_keep_all.txt",
         "hoststubgen_dump.txt",
     ],
-    // visibility:  ["//visibility:public"],
 }
 
 // Generate the stub/impl from framework-all, with hidden APIs.
@@ -111,8 +139,10 @@
 }
 
 // Extract the stub jar from "framework-all-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
 java_genrule_host {
     name: "framework-all-hidden-api-host-stub",
+    defaults: ["hoststubgen-for-prototype-only-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [
         ":framework-all-hidden-api-host{host_stub.jar}",
@@ -120,12 +150,13 @@
     out: [
         "host_stub.jar",
     ],
-    visibility: ["//visibility:public"],
 }
 
 // Extract the impl jar from "framework-all-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
 java_genrule_host {
     name: "framework-all-hidden-api-host-impl",
+    defaults: ["hoststubgen-for-prototype-only-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [
         ":framework-all-hidden-api-host{host_impl.jar}",
@@ -133,11 +164,11 @@
     out: [
         "host_impl.jar",
     ],
-    visibility: ["//visibility:public"],
 }
 
 // Generate the stub/impl from framework-all, with only public/system/test APIs, without
 // hidden APIs.
+// This is only for the prototype. Do not use it in "productionized" build rules.
 java_genrule_host {
     name: "framework-all-test-api-host",
     defaults: ["hoststubgen-command-defaults"],
@@ -154,8 +185,10 @@
 }
 
 // Extract the stub jar from "framework-all-test-api-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
 java_genrule_host {
     name: "framework-all-test-api-host-stub",
+    defaults: ["hoststubgen-for-prototype-only-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [
         ":framework-all-test-api-host{host_stub.jar}",
@@ -163,12 +196,13 @@
     out: [
         "host_stub.jar",
     ],
-    visibility: ["//visibility:public"],
 }
 
 // Extract the impl jar from "framework-all-test-api-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
 java_genrule_host {
     name: "framework-all-test-api-host-impl",
+    defaults: ["hoststubgen-for-prototype-only-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [
         ":framework-all-test-api-host{host_impl.jar}",
@@ -176,7 +210,6 @@
     out: [
         "host_impl.jar",
     ],
-    visibility: ["//visibility:public"],
 }
 
 // This library contains helper classes to build hostside tests/targets.
@@ -186,6 +219,7 @@
 // Ideally this library should be empty.
 java_library_host {
     name: "hoststubgen-helper-framework-buildtime",
+    defaults: ["hoststubgen-for-prototype-only-java"],
     srcs: [
         "helper-framework-buildtime-src/**/*.java",
     ],
@@ -195,13 +229,13 @@
         "framework-all-hidden-api-host-impl",
         "junit",
     ],
-    visibility: ["//visibility:public"],
 }
 
 // This module contains "fake" libcore/dalvik classes, framework native substitution, etc,
 // that are needed at runtime.
 java_library_host {
     name: "hoststubgen-helper-framework-runtime",
+    defaults: ["hoststubgen-for-prototype-only-java"],
     srcs: [
         "helper-framework-runtime-src/**/*.java",
     ],
@@ -209,7 +243,6 @@
         "hoststubgen-helper-runtime",
         "framework-all-hidden-api-host-impl",
     ],
-    visibility: ["//visibility:public"],
 }
 
 // Defaults for host side test modules.