Merge "Start Screenrecord from Record Issue QS Tile." into main
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
index 6d1e6d0..4352c8a 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -24,10 +24,11 @@
 import android.content.res.TypedArray
 import android.perftests.utils.BenchmarkState
 import android.perftests.utils.PerfStatusReporter
+import android.util.ArraySet
 import androidx.test.filters.LargeTest
+import com.android.internal.pm.parsing.pkg.PackageImpl
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
 import com.android.internal.util.ConcurrentUtils
-import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import java.io.File
 import java.io.FileOutputStream
 import java.util.concurrent.ArrayBlockingQueue
@@ -214,7 +215,10 @@
                     path,
                     manifestArray,
                     isCoreApp,
+                    this,
                 )
+                override fun getHiddenApiWhitelistedApps() = ArraySet<String>()
+                override fun getInstallConstraintsAllowlist() = ArraySet<String>()
             })
 
         override fun parseImpl(file: File) =
diff --git a/core/api/current.txt b/core/api/current.txt
index 7f261d4..1b5c953 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12606,6 +12606,7 @@
     method public boolean isStagedSessionApplied();
     method public boolean isStagedSessionFailed();
     method public boolean isStagedSessionReady();
+    method @FlaggedApi("android.content.pm.archiving") public boolean isUnarchival();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
     field public static final int INVALID_ID = -1; // 0xffffffff
@@ -13296,6 +13297,7 @@
     method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
     method @IntRange(from=0xffffffff) public long getLongVersion();
     method public String getName();
+    method @FlaggedApi("android.content.pm.sdk_lib_independence") @NonNull public java.util.List<android.content.pm.VersionedPackage> getOptionalDependentPackages();
     method public int getType();
     method @Deprecated @IntRange(from=0xffffffff) public int getVersion();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e7803fb..d395b8c 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -136,6 +136,7 @@
 package android.content.pm {
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    method @FlaggedApi("android.content.pm.sdk_lib_independence") @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getOptionalSharedLibraryInfos();
     method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraryInfos();
     field public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1; // 0xffffffff
     field public static final int HIDDEN_API_ENFORCEMENT_DISABLED = 0; // 0x0
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7237af3..e92564b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -12930,6 +12930,7 @@
     field public static final int CONFIDENCE_LEVEL_LOW = 1; // 0x1
     field public static final int CONFIDENCE_LEVEL_MEDIUM = 2; // 0x2
     field public static final int CONFIDENCE_LEVEL_NONE = 0; // 0x0
+    field @FlaggedApi("android.service.voice.flags.allow_hotword_bump_egress") public static final int CONFIDENCE_LEVEL_VERY_HIGH = 4; // 0x4
     field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordRejectedResult> CREATOR;
   }
 
@@ -13051,7 +13052,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.HotwordDetector.Callback);
     method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.VisualQueryDetector createVisualQueryDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VisualQueryDetector.Callback);
-    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setIsReceiveSandboxedTrainingDataAllowed(boolean);
+    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setShouldReceiveSandboxedTrainingData(boolean);
   }
 
 }
@@ -13701,6 +13702,7 @@
     method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method public int describeContents();
     method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers();
+    method @FlaggedApi("com.android.internal.telephony.flags.carrier_restriction_status") public int getCarrierRestrictionStatus();
     method public int getDefaultCarrierRestriction();
     method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
     method public int getMultiSimPolicy();
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index dec1ee5..cef11bb 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -1909,6 +1909,8 @@
     SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler):
     SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String):
+    SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams):
     SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 87c86df..287d2bd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2909,7 +2909,7 @@
         try {
             return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
                     launcherExtras, dialogInfo, flags, mContext.getOpPackageName(),
-                    getUserId());
+                    UserHandle.myUserId() /* suspendingUserId */, getUserId() /* targetUserId */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index d935449..53a21cd 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.annotation.DrawableRes;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -390,7 +392,7 @@
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public void setType(@Type int type) {
-        mType = type;
+        mType = checkValidType(type);
     }
 
     /**
@@ -451,6 +453,24 @@
         mAllowManualInvocation = allowManualInvocation;
     }
 
+    /** @hide */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public void validate() {
+        if (Flags.modesApi()) {
+            checkValidType(mType);
+        }
+    }
+
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @Type
+    private static int checkValidType(@Type int type) {
+        checkArgument(type >= TYPE_UNKNOWN && type <= TYPE_MANAGED,
+                "Rule type must be one of TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, "
+                        + "TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME, TYPE_DRIVING, TYPE_IMMERSIVE, "
+                        + "TYPE_THEATER, or TYPE_MANAGED");
+        return type;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -703,10 +723,10 @@
         }
 
         /**
-         * Sets the type of the rule
+         * Sets the type of the rule.
          */
         public @NonNull Builder setType(@Type int type) {
-            mType = type;
+            mType = checkValidType(type);
             return this;
         }
 
@@ -714,7 +734,7 @@
          * Sets a user visible description of when this rule will be active
          * (see {@link Condition#STATE_TRUE}).
          *
-         * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
+         * <p>A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
          * "When connected to [Car Name]".
          */
         public @NonNull Builder setTriggerDescription(@Nullable String description) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 16a80e9..3713380 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -19,6 +19,7 @@
 import static android.os.Build.VERSION_CODES.DONUT;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1065,11 +1066,25 @@
      * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
      * the structure.
      *
+     * NOTE: the list also contains the result of {@link #getOptionalSharedLibraryInfos}.
+     *
      * {@hide}
      */
+    @Nullable
     public List<SharedLibraryInfo> sharedLibraryInfos;
 
     /**
+     * List of all shared libraries this application is optionally linked against.
+     * This field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+     * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
+     * the structure.
+     *
+     * @hide
+     */
+    @Nullable
+    public List<SharedLibraryInfo> optionalSharedLibraryInfos;
+
+    /**
      * Full path to the default directory assigned to the package for its
      * persistent data.
      */
@@ -1937,6 +1952,7 @@
         seInfoUser = orig.seInfoUser;
         sharedLibraryFiles = orig.sharedLibraryFiles;
         sharedLibraryInfos = orig.sharedLibraryInfos;
+        optionalSharedLibraryInfos = orig.optionalSharedLibraryInfos;
         dataDir = orig.dataDir;
         deviceProtectedDataDir = orig.deviceProtectedDataDir;
         credentialProtectedDataDir = orig.credentialProtectedDataDir;
@@ -2029,6 +2045,7 @@
         dest.writeString8(seInfoUser);
         dest.writeString8Array(sharedLibraryFiles);
         dest.writeTypedList(sharedLibraryInfos);
+        dest.writeTypedList(optionalSharedLibraryInfos);
         dest.writeString8(dataDir);
         dest.writeString8(deviceProtectedDataDir);
         dest.writeString8(credentialProtectedDataDir);
@@ -2129,6 +2146,7 @@
         seInfoUser = source.readString8();
         sharedLibraryFiles = source.createString8Array();
         sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR);
+        optionalSharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR);
         dataDir = source.readString8();
         deviceProtectedDataDir = source.readString8();
         credentialProtectedDataDir = source.readString8();
@@ -2760,6 +2778,8 @@
      *  list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
      *  PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure.
      *
+     *  NOTE: the list also contains the result of {@link #getOptionalSharedLibraryInfos}.
+     *
      * @hide
      */
     @NonNull
@@ -2772,6 +2792,23 @@
     }
 
     /**
+     *  List of all shared libraries this application is optionally linked against. This
+     *  list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+     *  PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_SDK_LIB_INDEPENDENCE)
+    public List<SharedLibraryInfo> getOptionalSharedLibraryInfos() {
+        if (optionalSharedLibraryInfos == null) {
+            return Collections.EMPTY_LIST;
+        }
+        return optionalSharedLibraryInfos;
+    }
+
+    /**
      * Gets the trusted host certificate digests of apps that are allowed to embed activities of
      * this application. The digests are computed using the SHA-256 digest algorithm.
      * @see android.R.attr#knownActivityEmbeddingCerts
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 6f7299a..a97de63 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -65,6 +65,8 @@
             in UserHandle user);
     LauncherUserInfo getLauncherUserInfo(in UserHandle user);
     List<String> getPreInstalledSystemPackages(in UserHandle user);
+    IntentSender getAppMarketActivityIntent(String callingPackage, String packageName,
+            in UserHandle user);
     void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
             String callingFeatureId, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 98623de..6dc8d47 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -300,7 +300,8 @@
 
     String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
             in PersistableBundle appExtras, in PersistableBundle launcherExtras,
-            in SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId);
+            in SuspendDialogInfo dialogInfo, int flags, String suspendingPackage,
+            int suspendingUserId, int targetUserId);
 
     String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId);
 
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index ccc8f09..1d2b1af 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -799,6 +799,55 @@
         }
     }
 
+
+    /**
+     * Returns an intent sender which can be used to start the App Market activity (Installer
+     * Activity).
+     * This method is primarily used to get an intent sender which starts App Market activity for
+     * another profile, if the caller is not otherwise allowed to start activity in that profile.
+     *
+     * <p>When packageName is set, intent sender to start the App Market Activity which installed
+     * the package in calling user will be returned, but for the profile passed.
+     *
+     * <p>When packageName is not set, intent sender to launch the default App Market Activity for
+     * the profile will be returned. In case there are multiple App Market Activities available for
+     * the profile, IntentPicker will be started, allowing user to choose the preferred activity.
+     *
+     * <p>The method will fall back to the behaviour of not having the packageName set, in case:
+     * <ul>
+     *     <li>No activity for the packageName is found in calling user-space.</li>
+     *     <li>The App Market Activity which installed the package in calling user-space is not
+     *         present.</li>
+     *     <li>The App Market Activity which installed the package in calling user-space is not
+     *         present in the profile passed.</li>
+     * </ul>
+     * </p>
+     *
+     *
+     *
+     * @param packageName the package for which intent sender to launch App Market Activity is
+     *                    required.
+     * @param user the profile for which intent sender to launch App Market Activity is required.
+     * @return {@link IntentSender} object which launches the App Market Activity, null in case
+     *         there is no such activity.
+     * @hide
+     */
+    @Nullable
+    @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+    public IntentSender getAppMarketActivityIntent(@Nullable String packageName,
+            @NonNull UserHandle user) {
+        if (DEBUG) {
+            Log.i(TAG, "getAppMarketActivityIntent for package: " + packageName
+                    + " user: " + user);
+        }
+        try {
+            return mService.getAppMarketActivityIntent(mContext.getPackageName(),
+                    packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Returns the list of the system packages that are installed at user creation.
      *
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index e395127..0e131b4 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -4381,6 +4381,17 @@
             return pendingUserActionReason;
         }
 
+        /**
+         * Returns true if the session is an unarchival.
+         *
+         * @see PackageInstaller#requestUnarchive
+         */
+        @FlaggedApi(Flags.FLAG_ARCHIVING)
+        public boolean isUnarchival() {
+            return (installFlags & PackageManager.INSTALL_UNARCHIVE) != 0;
+        }
+
+
         @Override
         public int describeContents() {
             return 0;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a5d16f2..a8638708 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1517,6 +1517,7 @@
             INSTALL_REQUEST_UPDATE_OWNERSHIP,
             INSTALL_IGNORE_DEXOPT_PROFILE,
             INSTALL_UNARCHIVE_DRAFT,
+            INSTALL_UNARCHIVE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallFlags {}
@@ -1764,13 +1765,22 @@
      * If set, then the session is a draft session created for an upcoming unarchival by its
      * installer.
      *
-     * @see PackageInstaller#requestUnarchive(String)
+     * @see PackageInstaller#requestUnarchive
      *
      * @hide
      */
     public static final int INSTALL_UNARCHIVE_DRAFT = 1 << 29;
 
     /**
+     * If set, then the {@link PackageInstaller.Session} is an unarchival.
+     *
+     * @see PackageInstaller#requestUnarchive
+     *
+     * @hide
+     */
+    public static final int INSTALL_UNARCHIVE = 1 << 30;
+
+    /**
      * Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
      * a development-only feature and should not be used on end user devices.
      *
@@ -9990,6 +10000,9 @@
      * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
      * {@link android.Manifest.permission#SUSPEND_APPS}.
      *
+     * <p>
+     * <strong>Note:</strong>This API doesn't support cross user suspension and should only be used
+     * for testing.
      * @param suspendedPackage The package that has been suspended.
      * @return Name of the package that suspended the given package. Returns {@code null} if the
      * given package is not currently suspended and the platform package name - i.e.
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 25ba725..5acebf5 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -23,6 +24,7 @@
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -99,20 +101,22 @@
     private final boolean mIsNative;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
+
+    private final List<VersionedPackage> mOptionalDependentPackages;
     private List<SharedLibraryInfo> mDependencies;
 
     /**
      * Creates a new instance.
      *
-     * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of
-     *                  this shared library. Null for builtin library.
-     * @param name The lib name.
-     * @param version The lib version if not builtin.
-     * @param type The lib type.
-     * @param declaringPackage The package that declares the library.
+     * @param codePaths         For a non {@link #TYPE_BUILTIN builtin} library, the locations of
+     *                          jars of
+     *                          this shared library. Null for builtin library.
+     * @param name              The lib name.
+     * @param version           The lib version if not builtin.
+     * @param type              The lib type.
+     * @param declaringPackage  The package that declares the library.
      * @param dependentPackages The packages that depend on the library.
-     * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
-     *
+     * @param isNative          indicate if this shared lib is a native lib or not (i.e. java)
      * @hide
      */
     public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
@@ -129,6 +133,58 @@
         mDependentPackages = dependentPackages;
         mDependencies = dependencies;
         mIsNative = isNative;
+        mOptionalDependentPackages = null;
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of
+     *                  this shared library. Null for builtin library.
+     * @param name The lib name.
+     * @param version The lib version if not builtin.
+     * @param type The lib type.
+     * @param declaringPackage The package that declares the library.
+     * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
+     * @param allDependentPackages All packages that depend on the library (including the optional
+     *                             sdk libraries).
+     *
+     * @hide
+     */
+    public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+            String name, long version, int type,
+            VersionedPackage declaringPackage,
+            List<SharedLibraryInfo> dependencies, boolean isNative,
+            Pair<List<VersionedPackage>, List<Boolean>> allDependentPackages) {
+        mPath = path;
+        mPackageName = packageName;
+        mCodePaths = codePaths;
+        mName = name;
+        mVersion = version;
+        mType = type;
+        mDeclaringPackage = declaringPackage;
+        mDependencies = dependencies;
+        mIsNative = isNative;
+
+        var allDependents = allDependentPackages.first;
+        var usesLibOptional = allDependentPackages.second;
+        mDependentPackages = allDependents;
+        List<VersionedPackage> optionalDependents = null;
+        if (mType == SharedLibraryInfo.TYPE_SDK_PACKAGE
+                && Flags.sdkLibIndependence() && allDependents != null
+                && usesLibOptional != null
+                && allDependents.size() == usesLibOptional.size()) {
+            for (int k = 0; k < allDependents.size(); k++) {
+                VersionedPackage versionedPackage = allDependents.get(k);
+                if (usesLibOptional.get(k)) {
+                    if (optionalDependents == null) {
+                        optionalDependents = new ArrayList<>();
+                    }
+                    optionalDependents.add(versionedPackage);
+                }
+            }
+        }
+        mOptionalDependentPackages = optionalDependents;
     }
 
     private SharedLibraryInfo(Parcel parcel) {
@@ -148,6 +204,8 @@
                 parcel.readArrayList(null, android.content.pm.VersionedPackage.class);
         mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
         mIsNative = parcel.readBoolean();
+        mOptionalDependentPackages = parcel.readParcelableList(new ArrayList<>(),
+                VersionedPackage.class.getClassLoader(), VersionedPackage.class);
     }
 
     /**
@@ -324,6 +382,8 @@
     /**
      * Gets the packages that depend on the library.
      *
+     * NOTE: the list also contains the result of {@link #getOptionalDependentPackages}.
+     *
      * @return The dependent packages.
      */
     public @NonNull List<VersionedPackage> getDependentPackages() {
@@ -333,6 +393,19 @@
         return mDependentPackages;
     }
 
+    /**
+     * Gets the packages that optionally depend on the library.
+     *
+     * @return The dependent packages.
+     */
+    @FlaggedApi(Flags.FLAG_SDK_LIB_INDEPENDENCE)
+    public @NonNull List<VersionedPackage> getOptionalDependentPackages() {
+        if (mOptionalDependentPackages == null) {
+            return Collections.emptyList();
+        }
+        return mOptionalDependentPackages;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -362,6 +435,7 @@
         parcel.writeList(mDependentPackages);
         parcel.writeTypedList(mDependencies);
         parcel.writeBoolean(mIsNative);
+        parcel.writeParcelableList(mOptionalDependentPackages, flags);
     }
 
     private static String typeToString(int type) {
diff --git a/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl b/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl
new file mode 100644
index 0000000..73ac333
--- /dev/null
+++ b/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+/**
+ * Low-level callback interface between <Biometric>Manager and <Auth>Service. Allows core system
+ * services (e.g. SystemUI) to register a listener for updates about the current state of biometric
+ * authentication.
+ * @hide
+ */
+oneway interface AuthenticationStateListener {
+    /**
+     * Defines behavior in response to authentication starting
+     * @param requestReason reason from [BiometricRequestConstants.RequestReason] for requesting
+     * authentication starting
+     */
+    void onAuthenticationStarted(int requestReason);
+
+    /**
+     * Defines behavior in response to authentication stopping
+     */
+    void onAuthenticationStopped();
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index f82f79e..d7d1d1a 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -552,6 +552,44 @@
     }
 
     /**
+     * Registers listener for changes to biometric authentication state.
+     * Only sends callbacks for events that occur after the callback has been registered.
+     * @param listener Listener for changes to biometric authentication state
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void registerAuthenticationStateListener(AuthenticationStateListener listener) {
+        if (mService != null) {
+            try {
+                mService.registerAuthenticationStateListener(listener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "registerAuthenticationStateListener(): Service not connected");
+        }
+    }
+
+    /**
+     * Unregisters listener for changes to biometric authentication state.
+     * @param listener Listener for changes to biometric authentication state
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void unregisterAuthenticationStateListener(AuthenticationStateListener listener) {
+        if (mService != null) {
+            try {
+                mService.unregisterAuthenticationStateListener(listener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "unregisterAuthenticationStateListener(): Service not connected");
+        }
+    }
+
+
+    /**
      * Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their
      * authenticatorId invalidated for the specified user. This happens when enrollments have been
      * added on devices with multiple biometric sensors.
diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricRequestConstants.java
similarity index 69%
rename from core/java/android/hardware/biometrics/BiometricOverlayConstants.java
rename to core/java/android/hardware/biometrics/BiometricRequestConstants.java
index 065ae64a..b036f30 100644
--- a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricRequestConstants.java
@@ -22,24 +22,27 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Common constants for biometric overlays.
+ * Common constants for biometric requests.
  * @hide
  */
-public interface BiometricOverlayConstants {
+public class BiometricRequestConstants {
+
+    private BiometricRequestConstants() {}
+
     /** Unknown usage. */
-    int REASON_UNKNOWN = 0;
+    public static final int REASON_UNKNOWN = 0;
     /** User is about to enroll. */
-    int REASON_ENROLL_FIND_SENSOR = 1;
+    public static final int REASON_ENROLL_FIND_SENSOR = 1;
     /** User is enrolling. */
-    int REASON_ENROLL_ENROLLING = 2;
+    public static final int REASON_ENROLL_ENROLLING = 2;
     /** Usage from BiometricPrompt. */
-    int REASON_AUTH_BP = 3;
-    /** Usage from Keyguard. */
-    int REASON_AUTH_KEYGUARD = 4;
+    public static final int REASON_AUTH_BP = 3;
+    /** Usage from Device Entry. */
+    public static final int REASON_AUTH_KEYGUARD = 4;
     /** Non-specific usage (from FingerprintManager). */
-    int REASON_AUTH_OTHER = 5;
+    public static final int REASON_AUTH_OTHER = 5;
     /** Usage from Settings. */
-    int REASON_AUTH_SETTINGS = 6;
+    public static final int REASON_AUTH_SETTINGS = 6;
 
     @IntDef({REASON_UNKNOWN,
             REASON_ENROLL_FIND_SENSOR,
@@ -49,5 +52,5 @@
             REASON_AUTH_OTHER,
             REASON_AUTH_SETTINGS})
     @Retention(RetentionPolicy.SOURCE)
-    @interface ShowReason {}
+    public @interface RequestReason {}
 }
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 5bdbe2b5..8514f98 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.biometrics;
 
+import android.hardware.biometrics.AuthenticationStateListener;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 import android.hardware.biometrics.IInvalidationCallback;
@@ -66,6 +67,12 @@
     // Register callback for when keyguard biometric eligibility changes.
     void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
 
+    // Register listener for changes to authentication state.
+    void registerAuthenticationStateListener(AuthenticationStateListener listener);
+
+    // Unregister listener for changes to authentication state.
+    void unregisterAuthenticationStateListener(AuthenticationStateListener listener);
+
     // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
     // specified user. This happens when enrollments have been added on devices with multiple
     // biometric sensors.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 93fbe8a..7cf10d8 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1216,7 +1216,7 @@
      * <ul>
      * <li>Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }</li>
      * <li>All mandatory stream combinations for this specific capability as per
-     *   <a href="CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li>
+     *   <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li>
      * <li>In case the device is not able to capture some combination of supported
      *   standard 8-bit and/or 10-bit dynamic range profiles within the same capture request,
      *   then those constraints must be listed in
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 06397c9..ded96a2 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1484,7 +1484,7 @@
      * <p>To start a CaptureSession with a target FPS range different from the
      * capture request template's default value, the application
      * is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the target fps range before creating the capture session. The aeTargetFpsRange is
      * typically a session parameter. Specifying it at session creation time helps avoid
      * session reconfiguration delays in cases like 60fps or high speed recording.</p>
@@ -2161,7 +2161,7 @@
      * OFF if the recording output is not stabilized, or if there are no output
      * Surface types that can be stabilized.</p>
      * <p>The application is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the desired video stabilization mode before creating the capture session.
      * Video stabilization mode is a session parameter on many devices. Specifying
      * it at session creation time helps avoid reconfiguration delay caused by difference
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ab4406c3..1d26d69 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -899,7 +899,7 @@
      * <p>To start a CaptureSession with a target FPS range different from the
      * capture request template's default value, the application
      * is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the target fps range before creating the capture session. The aeTargetFpsRange is
      * typically a session parameter. Specifying it at session creation time helps avoid
      * session reconfiguration delays in cases like 60fps or high speed recording.</p>
@@ -2382,7 +2382,7 @@
      * OFF if the recording output is not stabilized, or if there are no output
      * Surface types that can be stabilized.</p>
      * <p>The application is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the desired video stabilization mode before creating the capture session.
      * Video stabilization mode is a session parameter on many devices. Specifying
      * it at session creation time helps avoid reconfiguration delay caused by difference
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 935157a..fe7de83 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -983,6 +983,7 @@
         }
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /**
      * @hide
      */
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 0100660..f594c00 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.fingerprint;
 
+import android.hardware.biometrics.AuthenticationStateListener;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricStateListener;
@@ -203,6 +204,14 @@
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     void setSidefpsController(in ISidefpsController controller);
 
+    // Registers AuthenticationStateListener.
+    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+    void registerAuthenticationStateListener(AuthenticationStateListener listener);
+
+    // Unregisters AuthenticationStateListener.
+    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+    void unregisterAuthenticationStateListener(AuthenticationStateListener listener);
+
     // Registers BiometricStateListener.
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     void registerBiometricStateListener(IBiometricStateListener listener);
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 531626b1..6e771f8 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2014, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +16,8 @@
 
 package android.service.notification;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -117,7 +119,7 @@
 
     /** The source of, or reason for, the state change represented by this Condition. **/
     @FlaggedApi(Flags.FLAG_MODES_API)
-    public final @Source int source;
+    public final @Source int source; // default = SOURCE_UNKNOWN
 
     /**
      * The maximum string length for any string contained in this condition.
@@ -179,7 +181,7 @@
         this.line2 = getTrimmedString(line2);
         this.icon = icon;
         this.state = state;
-        this.source = source;
+        this.source = checkValidSource(source);
         this.flags = flags;
     }
 
@@ -197,10 +199,26 @@
                 source.readInt());
     }
 
+    /** @hide */
+    public void validate() {
+        if (Flags.modesApi()) {
+            checkValidSource(source);
+        }
+    }
+
     private static boolean isValidState(int state) {
         return state >= STATE_FALSE && state <= STATE_ERROR;
     }
 
+    private static int checkValidSource(@Source int source) {
+        if (Flags.modesApi()) {
+            checkArgument(source >= SOURCE_UNKNOWN && source <= SOURCE_CONTEXT,
+                    "Condition source must be one of SOURCE_UNKNOWN, SOURCE_USER_ACTION, "
+                            + "SOURCE_SCHEDULE, or SOURCE_CONTEXT");
+        }
+        return source;
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(id, 0);
diff --git a/core/java/android/service/voice/HotwordRejectedResult.java b/core/java/android/service/voice/HotwordRejectedResult.java
index 26c1ca4..eb1ac67 100644
--- a/core/java/android/service/voice/HotwordRejectedResult.java
+++ b/core/java/android/service/voice/HotwordRejectedResult.java
@@ -16,9 +16,11 @@
 
 package android.service.voice;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.os.Parcelable;
+import android.service.voice.flags.Flags;
 
 import com.android.internal.util.DataClass;
 
@@ -53,12 +55,17 @@
     /** High confidence in hotword detector result. */
     public static final int CONFIDENCE_LEVEL_HIGH = 3;
 
+    /** Very high confidence in hotword detector result. **/
+    @FlaggedApi(Flags.FLAG_ALLOW_HOTWORD_BUMP_EGRESS)
+    public static final int CONFIDENCE_LEVEL_VERY_HIGH = 4;
+
     /** @hide */
     @IntDef(prefix = {"CONFIDENCE_LEVEL_"}, value = {
             CONFIDENCE_LEVEL_NONE,
             CONFIDENCE_LEVEL_LOW,
             CONFIDENCE_LEVEL_MEDIUM,
-            CONFIDENCE_LEVEL_HIGH
+            CONFIDENCE_LEVEL_HIGH,
+            CONFIDENCE_LEVEL_VERY_HIGH
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface HotwordConfidenceLevelValue {
@@ -91,9 +98,10 @@
         CONFIDENCE_LEVEL_NONE,
         CONFIDENCE_LEVEL_LOW,
         CONFIDENCE_LEVEL_MEDIUM,
-        CONFIDENCE_LEVEL_HIGH
+        CONFIDENCE_LEVEL_HIGH,
+        CONFIDENCE_LEVEL_VERY_HIGH
     })
-    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @Retention(RetentionPolicy.SOURCE)
     @DataClass.Generated.Member
     public @interface ConfidenceLevel {}
 
@@ -109,6 +117,8 @@
                     return "CONFIDENCE_LEVEL_MEDIUM";
             case CONFIDENCE_LEVEL_HIGH:
                     return "CONFIDENCE_LEVEL_HIGH";
+            case CONFIDENCE_LEVEL_VERY_HIGH:
+                    return "CONFIDENCE_LEVEL_VERY_HIGH";
             default: return Integer.toHexString(value);
         }
     }
@@ -259,10 +269,10 @@
     }
 
     @DataClass.Generated(
-            time = 1621961370106L,
+            time = 1701990933632L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/voice/HotwordRejectedResult.java",
-            inputSignatures = "public static final  int CONFIDENCE_LEVEL_NONE\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_HIGH\nprivate final @android.service.voice.HotwordRejectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate static  int defaultConfidenceLevel()\nclass HotwordRejectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+            inputSignatures = "public static final  int CONFIDENCE_LEVEL_NONE\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_HIGH\npublic static final @android.annotation.FlaggedApi int CONFIDENCE_LEVEL_VERY_HIGH\nprivate final @android.service.voice.HotwordRejectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate static  int defaultConfidenceLevel()\nclass HotwordRejectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index fba0923..75ab48a 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -1042,13 +1042,13 @@
     @SystemApi
     @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
     @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
-    public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) {
-        Log.i(TAG, "setIsReceiveSandboxedTrainingDataAllowed to " + allowed);
+    public void setShouldReceiveSandboxedTrainingData(boolean allowed) {
+        Log.i(TAG, "setShouldReceiveSandboxedTrainingData to " + allowed);
         if (mSystemService == null) {
             throw new IllegalStateException("Not available until onReady() is called");
         }
         try {
-            mSystemService.setIsReceiveSandboxedTrainingDataAllowed(allowed);
+            mSystemService.setShouldReceiveSandboxedTrainingData(allowed);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
index c414ef8..b596666 100644
--- a/core/java/android/service/voice/flags/flags.aconfig
+++ b/core/java/android/service/voice/flags/flags.aconfig
@@ -6,3 +6,10 @@
     description: "This flag allows the hotword detection service to egress training data to the default assistant."
     bug: "296074924"
 }
+
+flag {
+    name: "allow_hotword_bump_egress"
+    namespace: "machine_learning"
+    description: "This flag allows hotword detection service to egress reason code for hotword bump."
+    bug: "290951024"
+}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index dbacca5..9bf43a3 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -162,6 +162,8 @@
      * @param flags See {@code View#startDragAndDrop}
      * @param surface Surface containing drag shadow image
      * @param touchSource See {@code InputDevice#getSource()}
+     * @param touchDeviceId device ID of last touch event
+     * @param pointerId pointer ID of last touch event
      * @param touchX X coordinate of last touch point
      * @param touchY Y coordinate of last touch point
      * @param thumbCenterX X coordinate for the position within the shadow image that should be
@@ -171,9 +173,9 @@
      * @param data Data transferred by drag and drop
      * @return Token of drag operation which will be passed to cancelDragAndDrop.
      */
-    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     IBinder performDrag(IWindow window, int flags, in SurfaceControl surface, int touchSource,
-            float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data);
+            int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX,
+            float thumbCenterY, in ClipData data);
 
     /**
      * Drops the content of the current drag operation for accessibility
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 75f8eba..bb5ee03 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28340,6 +28340,8 @@
                 IBinder token = mAttachInfo.mSession.performDrag(
                         mAttachInfo.mWindow, flags, null,
                         mAttachInfo.mViewRootImpl.getLastTouchSource(),
+                        mAttachInfo.mViewRootImpl.getLastTouchDeviceId(),
+                        mAttachInfo.mViewRootImpl.getLastTouchPointerId(),
                         0f, 0f, 0f, 0f, data);
                 if (ViewDebug.DEBUG_DRAG) {
                     Log.d(VIEW_LOG_TAG, "startDragAndDrop via a11y action returned " + token);
@@ -28414,7 +28416,8 @@
             }
 
             token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
-                    root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y,
+                    root.getLastTouchSource(), root.getLastTouchDeviceId(),
+                    root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
                     shadowTouchPoint.x, shadowTouchPoint.y, data);
             if (ViewDebug.DEBUG_DRAG) {
                 Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8339d33..e83488e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -813,6 +813,8 @@
     final PointF mDragPoint = new PointF();
     final PointF mLastTouchPoint = new PointF();
     int mLastTouchSource;
+    int mLastTouchDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
+    int mLastTouchPointerId;
     /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/
     private int mLastClickToolType;
 
@@ -7163,6 +7165,8 @@
                 mLastTouchPoint.x = event.getRawX();
                 mLastTouchPoint.y = event.getRawY();
                 mLastTouchSource = event.getSource();
+                mLastTouchDeviceId = event.getDeviceId();
+                mLastTouchPointerId = event.getPointerId(0);
 
                 // Register last ACTION_UP. This will be propagated to IME.
                 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -8574,6 +8578,14 @@
         return mLastTouchSource;
     }
 
+    public int getLastTouchDeviceId() {
+        return mLastTouchDeviceId;
+    }
+
+    public int getLastTouchPointerId() {
+        return mLastTouchPointerId;
+    }
+
     /**
      * Used by InputMethodManager.
      * @hide
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c7e1807..e3d9c60 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -592,6 +592,13 @@
     int TRANSIT_FLAG_KEYGUARD_UNOCCLUDING = (1 << 13); // 0x2000
 
     /**
+     * Transition flag: Indicates that there is a physical display switch
+     * TODO(b/316112906) remove after defer_display_updates flag roll out
+     * @hide
+     */
+    int TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH = (1 << 14); // 0x4000
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -608,7 +615,8 @@
             TRANSIT_FLAG_INVISIBLE,
             TRANSIT_FLAG_KEYGUARD_APPEARING,
             TRANSIT_FLAG_KEYGUARD_OCCLUDING,
-            TRANSIT_FLAG_KEYGUARD_UNOCCLUDING
+            TRANSIT_FLAG_KEYGUARD_UNOCCLUDING,
+            TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionFlags {}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 393d256..d6ac562 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -489,8 +489,9 @@
 
     @Override
     public android.os.IBinder performDrag(android.view.IWindow window, int flags,
-            android.view.SurfaceControl surface, int touchSource, float touchX, float touchY,
-            float thumbCenterX, float thumbCenterY, android.content.ClipData data) {
+            android.view.SurfaceControl surface, int touchSource, int touchDeviceId,
+            int touchPointerId, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+            android.content.ClipData data) {
         return null;
     }
 
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 82ee8fc..e92c6a6 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -397,5 +397,5 @@
       * sandboxed detection (from trusted process).
       */
       @EnforcePermission("MANAGE_HOTWORD_DETECTION")
-      void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed);
+      void setShouldReceiveSandboxedTrainingData(boolean allowed);
 }
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index efc1455..f1aa330 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -39,6 +39,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -308,7 +309,8 @@
                         try {
                             final String[] errored = ipm.setPackagesSuspendedAsUser(
                                     new String[]{mSuspendedPackage}, false, null, null, null, 0,
-                                    mSuspendingPackage, mUserId);
+                                    mSuspendingPackage, mUserId /* suspendingUserId */,
+                                    mUserId /* targetUserId */);
                             if (ArrayUtils.contains(errored, mSuspendedPackage)) {
                                 Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage);
                                 break;
@@ -350,17 +352,18 @@
     }
 
     public static Intent createSuspendedAppInterceptIntent(String suspendedPackage,
-            String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options,
+            UserPackage suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options,
             IntentSender onUnsuspend, int userId) {
-        return new Intent()
+        Intent intent = new Intent()
                 .setClassName("android", SuspendedAppActivity.class.getName())
                 .putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
                 .putExtra(EXTRA_DIALOG_INFO, dialogInfo)
-                .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
+                .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage.packageName)
                 .putExtra(EXTRA_UNSUSPEND_INTENT, onUnsuspend)
                 .putExtra(EXTRA_ACTIVITY_OPTIONS, options)
                 .putExtra(Intent.EXTRA_USER_ID, userId)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        return intent;
     }
 }
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index f460233..96740c5 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -109,6 +109,7 @@
      * eg: Exit the app using back gesture.
      */
     public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78;
+    // 79 is reserved.
     public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80;
     public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81;
 
@@ -119,10 +120,11 @@
     public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
     public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
     public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
+    public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
 
     // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
     @VisibleForTesting
-    static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME;
+    static final int LAST_CUJ = CUJ_LAUNCHER_SEARCH_QSB_OPEN;
 
     /** @hide */
     @IntDef({
@@ -204,6 +206,7 @@
             CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
             CUJ_PREDICTIVE_BACK_CROSS_TASK,
             CUJ_PREDICTIVE_BACK_HOME,
+            CUJ_LAUNCHER_SEARCH_QSB_OPEN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -295,6 +298,8 @@
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
+        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] =
+            FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
     }
 
     private Cuj() {
@@ -467,6 +472,8 @@
                 return "PREDICTIVE_BACK_CROSS_TASK";
             case CUJ_PREDICTIVE_BACK_HOME:
                 return "PREDICTIVE_BACK_HOME";
+            case CUJ_LAUNCHER_SEARCH_QSB_OPEN:
+                return "LAUNCHER_SEARCH_QSB_OPEN";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/net/TEST_MAPPING b/core/java/com/android/internal/net/TEST_MAPPING
index 971ad36..f935946c 100644
--- a/core/java/com/android/internal/net/TEST_MAPPING
+++ b/core/java/com/android/internal/net/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksNetTests",
       "options": [
diff --git a/core/java/com/android/internal/pm/parsing/AppInfoUtils.java b/core/java/com/android/internal/pm/parsing/AppInfoUtils.java
new file mode 100644
index 0000000..38a2fe2
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/AppInfoUtils.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.pm.parsing;
+
+import android.annotation.CheckResult;
+import android.content.pm.ApplicationInfo;
+
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.AndroidPackage;
+
+public class AppInfoUtils {
+
+    /**
+     * @see ApplicationInfo#flags
+     */
+    public static int appInfoFlags(AndroidPackage pkg) {
+        // @formatter:off
+        int pkgWithoutStateFlags = flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE)
+                | flag(pkg.isHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED)
+                | flag(pkg.isBackupAllowed(), ApplicationInfo.FLAG_ALLOW_BACKUP)
+                | flag(pkg.isKillAfterRestoreAllowed(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
+                | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+                | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY)
+                | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT)
+                | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE)
+                | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE)
+                | flag(pkg.isDeclaredHavingCode(), ApplicationInfo.FLAG_HAS_CODE)
+                | flag(pkg.isTaskReparentingAllowed(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
+                | flag(pkg.isClearUserDataAllowed(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
+                | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP)
+                | flag(pkg.isCleartextTrafficAllowed(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
+                | flag(pkg.isRtlSupported(), ApplicationInfo.FLAG_SUPPORTS_RTL)
+                | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY)
+                | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH)
+                | flag(pkg.isExtractNativeLibrariesRequested(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS)
+                | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME)
+                | flag(pkg.isSmallScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS)
+                | flag(pkg.isNormalScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS)
+                | flag(pkg.isLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS)
+                | flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
+                | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
+                | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
+                | flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM)
+                | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST);
+
+        return pkgWithoutStateFlags;
+        // @formatter:on
+    }
+
+    /** @see ApplicationInfo#privateFlags */
+    public static int appInfoPrivateFlags(AndroidPackage pkg) {
+        // @formatter:off
+        int pkgWithoutStateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY)
+                | flag(pkg.isResourceOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY)
+                | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING)
+                | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS)
+                | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL)
+                | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND)
+                | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX)
+                | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
+                | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE)
+                | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE)
+                | flag(pkg.isClearUserDataOnFailedRestoreAllowed(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE)
+                | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE)
+                | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
+                | flag(pkg.isNonSdkApiRequested(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API)
+                | flag(pkg.isUserDataFragile(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
+                | flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+                | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
+                | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING)
+                | flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
+                | flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+                | flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM)
+                | flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR)
+                | flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
+                | flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM)
+                | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
+
+        Boolean resizeableActivity = pkg.getResizeableActivity();
+        if (resizeableActivity != null) {
+            if (resizeableActivity) {
+                pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+            } else {
+                pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+            }
+        }
+
+        return pkgWithoutStateFlags;
+        // @formatter:on
+    }
+
+
+    /** @see ApplicationInfo#privateFlagsExt */
+    public static int appInfoPrivateFlagsExt(AndroidPackage pkg,
+            boolean isAllowlistedForHiddenApis) {
+        // @formatter:off
+        int pkgWithoutStateFlags = flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE)
+                | flag(pkg.hasRequestForegroundServiceExemption(), ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION)
+                | flag(pkg.isAttributionsUserVisible(), ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE)
+                | flag(pkg.isOnBackInvokedCallbackEnabled(), ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK)
+                | flag(isAllowlistedForHiddenApis, ApplicationInfo.PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS);
+        return pkgWithoutStateFlags;
+        // @formatter:on
+    }
+
+    @CheckResult
+    private static int flag(boolean hasFlag, int flag) {
+        return hasFlag ? flag : 0;
+    }
+}
diff --git a/core/java/com/android/internal/pm/parsing/PackageParserException.java b/core/java/com/android/internal/pm/parsing/PackageParserException.java
new file mode 100644
index 0000000..4250bbd
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/PackageParserException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.pm.parsing;
+
+public class PackageParserException extends Exception {
+    public final int error;
+
+    public PackageParserException(int error, String detailMessage) {
+        super(detailMessage);
+        this.error = error;
+    }
+
+    public PackageParserException(int error, String detailMessage, Throwable throwable) {
+        super(detailMessage, throwable);
+        this.error = error;
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java
new file mode 100644
index 0000000..e65f1c9
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.pm.parsing.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
+import com.android.server.pm.pkg.AndroidPackage;
+
+/** @hide */
+public class AndroidPackageLegacyUtils {
+
+    private AndroidPackageLegacyUtils() {
+    }
+
+    /**
+     * Returns the primary ABI as parsed from the package. Used only during parsing and derivation.
+     * Otherwise prefer {@link PackageState#getPrimaryCpuAbi()}.
+     */
+    public static String getRawPrimaryCpuAbi(AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).getPrimaryCpuAbi();
+    }
+
+    /**
+     * Returns the secondary ABI as parsed from the package. Used only during parsing and
+     * derivation. Otherwise prefer {@link PackageState#getSecondaryCpuAbi()}.
+     */
+    public static String getRawSecondaryCpuAbi(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).getSecondaryCpuAbi();
+    }
+
+    @Deprecated
+    @NonNull
+    public static ApplicationInfo generateAppInfoWithoutState(AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).toAppInfoWithoutState();
+    }
+
+    /**
+     * Replacement of unnecessary legacy getRealPackage. Only returns a value if the package was
+     * actually renamed.
+     */
+    @Nullable
+    public static String getRealPackageOrNull(@NonNull AndroidPackage pkg, boolean isSystem) {
+        if (pkg.getOriginalPackages().isEmpty() || !isSystem) {
+            return null;
+        }
+
+        return pkg.getManifestPackageName();
+    }
+
+    public static void fillVersionCodes(@NonNull AndroidPackage pkg, @NonNull PackageInfo info) {
+        info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
+        info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isSystem}
+     */
+    @Deprecated
+    public static boolean isSystem(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isSystem();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isSystemExt}
+     */
+    @Deprecated
+    public static boolean isSystemExt(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isSystemExt();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isPrivileged}
+     */
+    @Deprecated
+    public static boolean isPrivileged(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isPrivileged();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isOem}
+     */
+    @Deprecated
+    public static boolean isOem(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isOem();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isVendor}
+     */
+    @Deprecated
+    public static boolean isVendor(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isVendor();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isProduct}
+     */
+    @Deprecated
+    public static boolean isProduct(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isProduct();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isOdm}
+     */
+    @Deprecated
+    public static boolean isOdm(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isOdm();
+    }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
similarity index 98%
rename from services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
rename to core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index da58d47..f7e1f72 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
 
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
@@ -50,47 +50,44 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
-import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
-import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.AppInfoUtils;
 import com.android.internal.pm.pkg.AndroidPackageSplitImpl;
+import com.android.internal.pm.pkg.SEInfoUtil;
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
 import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedActivityImpl;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedApexSystemServiceImpl;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedAttributionImpl;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl;
+import com.android.internal.pm.pkg.component.ParsedPermissionImpl;
 import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProcessImpl;
 import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedProviderImpl;
 import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedServiceImpl;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.AndroidPackageSplit;
-import com.android.server.pm.pkg.SELinuxUtil;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl;
-import com.android.server.pm.pkg.component.ParsedAttributionImpl;
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
-import com.android.server.pm.pkg.component.ParsedPermissionImpl;
-import com.android.server.pm.pkg.component.ParsedProcessImpl;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedServiceImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import libcore.util.EmptyArray;
 
@@ -422,8 +419,10 @@
 
     @NonNull
     public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
-            @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) {
-        return new PackageImpl(packageName, baseCodePath, codePath, manifestArray, isCoreApp);
+            @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp,
+            @Nullable ParsingPackageUtils.Callback callback) {
+        return new PackageImpl(
+                packageName, baseCodePath, codePath, manifestArray, isCoreApp, callback);
     }
 
     /**
@@ -453,7 +452,7 @@
     @NonNull
     @VisibleForTesting
     public static ParsingPackage forTesting(String packageName, String baseCodePath) {
-        return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false);
+        return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false, null);
     }
 
     @NonNull
@@ -2694,12 +2693,16 @@
     private String mBaseAppDataCredentialProtectedDirForSystemUser;
     private String mBaseAppDataDeviceProtectedDirForSystemUser;
 
+    ParsingPackageUtils.Callback mCallback;
+
     @VisibleForTesting
     public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
-            @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) {
+            @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp,
+            @Nullable ParsingPackageUtils.Callback callback) {
         this.packageName = TextUtils.safeIntern(packageName);
         this.mBaseApkPath = baseApkPath;
         this.mPath = path;
+        this.mCallback = callback;
 
         if (manifestArray != null) {
             versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
@@ -2750,9 +2753,11 @@
     }
 
     private void assignDerivedFields2() {
-        mBaseAppInfoFlags = PackageInfoUtils.appInfoFlags(this, null);
-        mBaseAppInfoPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null);
-        mBaseAppInfoPrivateFlagsExt = PackageInfoUtils.appInfoPrivateFlagsExt(this, null);
+        mBaseAppInfoFlags = AppInfoUtils.appInfoFlags(this);
+        mBaseAppInfoPrivateFlags = AppInfoUtils.appInfoPrivateFlags(this);
+        mBaseAppInfoPrivateFlagsExt = AppInfoUtils.appInfoPrivateFlagsExt(this,
+                mCallback == null ? false :
+                        mCallback.getHiddenApiWhitelistedApps().contains(this.packageName));
         String baseAppDataDir = Environment.getDataDirectoryPath(getVolumeUuid()) + File.separator;
         String systemUserSuffix = File.separator + UserHandle.USER_SYSTEM + File.separator;
         mBaseAppDataCredentialProtectedDirForSystemUser = TextUtils.safeIntern(
@@ -3087,7 +3092,7 @@
         appInfo.primaryCpuAbi = primaryCpuAbi;
         appInfo.secondaryCpuAbi = secondaryCpuAbi;
         appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
-        appInfo.seInfoUser = SELinuxUtil.COMPLETE_STR;
+        appInfo.seInfoUser = SEInfoUtil.COMPLETE_STR;
         appInfo.uid = uid;
         return appInfo;
     }
diff --git a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java b/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java
similarity index 85%
rename from services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java
rename to core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java
index d962505..a670c6d 100644
--- a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java
+++ b/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.permission;
+package com.android.internal.pm.permission;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -67,7 +67,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -97,10 +97,10 @@
     }
 
     @DataClass.Generated(
-            time = 1627674427184L,
+            time = 1701338392152L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final  int mSdkVersion\npublic static final  android.content.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final  int mSdkVersion\npublic static final  com.android.internal.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/pm/pkg/SEInfoUtil.java b/core/java/com/android/internal/pm/pkg/SEInfoUtil.java
new file mode 100644
index 0000000..a698882
--- /dev/null
+++ b/core/java/com/android/internal/pm/pkg/SEInfoUtil.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.pm.pkg;
+
+/**
+ * Utility methods that need to be used in application space.
+ * @hide
+ */
+public final class SEInfoUtil {
+
+    /** Append to existing seinfo label for instant apps @hide */
+    public static final String INSTANT_APP_STR = ":ephemeralapp";
+
+    /** Append to existing seinfo when modifications are complete @hide */
+    public static final String COMPLETE_STR = ":complete";
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java
similarity index 88%
rename from services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java
index 1964df0..fd5f0f0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java
@@ -14,19 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import com.android.internal.pm.pkg.component.ParsedActivity;
-import com.android.internal.pm.pkg.component.ParsedComponent;
-import com.android.internal.pm.pkg.component.ParsedMainComponent;
-import com.android.internal.pm.pkg.component.ParsedPermission;
-import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
-import com.android.internal.pm.pkg.component.ParsedProcess;
-import com.android.internal.pm.pkg.component.ParsedProvider;
-
 /**
  * Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually
  * be removed once all post-parsing mutation is moved to parsing.
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
similarity index 85%
rename from services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
index 019ca13..db08005 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.AttrRes;
 import android.annotation.NonNull;
@@ -29,14 +29,9 @@
 import android.content.res.XmlResourceParser;
 import android.text.TextUtils;
 
-import com.android.internal.pm.pkg.component.ParsedComponent;
-import com.android.internal.pm.pkg.component.ParsedIntentInfo;
-import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -173,16 +168,4 @@
     public static int getIcon(ParsedComponent component) {
         return component.getIcon();
     }
-
-    public static boolean isMatch(PackageUserState state, boolean isSystem,
-            boolean isPackageEnabled, ParsedMainComponent component, long flags) {
-        return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled,
-                component.isEnabled(), component.isDirectBootAware(), component.getName(), flags);
-    }
-
-    public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled,
-            ParsedMainComponent parsedComponent, long flags) {
-        return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
-                parsedComponent.getName(), flags);
-    }
 }
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
rename to core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
index dd54cfc..0b04591 100644
--- a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
+++ b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 
 import android.content.pm.parsing.result.ParseInput;
@@ -27,7 +27,6 @@
 
 import com.android.internal.R;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.SystemConfig;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -48,9 +47,8 @@
      * @hide
      */
     public static ParseResult<ParsingPackage> parseInstallConstraints(
-            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
-        Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
+            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser,
+            Set<String> allowlist) throws XmlPullParserException, IOException {
         if (!allowlist.contains(pkg.getPackageName())) {
             return input.skip("install-constraints cannot be used by this package");
         }
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
similarity index 92%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
index f027901..2f977ee 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
@@ -22,8 +22,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString;
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForStringSet;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForStringSet;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -36,10 +36,9 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
-import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import java.util.Collections;
 import java.util.Locale;
@@ -380,7 +379,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -696,10 +695,10 @@
     }
 
     @DataClass.Generated(
-            time = 1669437519576L,
+            time = 1701338377709L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
-            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java",
+            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
similarity index 98%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
index 64985bd..c3f7dab 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
-import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
-import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
-import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
+import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -48,11 +48,10 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java
index cfed19a..27f7eee 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 
 import android.annotation.NonNull;
@@ -22,7 +22,6 @@
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedApexSystemService;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 
@@ -60,7 +59,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -248,9 +247,9 @@
     };
 
     @DataClass.Generated(
-            time = 1643723578605L,
+            time = 1701710844088L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java",
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java",
             inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate  int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
     @Deprecated
     private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java
index d3fb29b..c69213f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.R;
 import android.annotation.NonNull;
@@ -25,8 +25,6 @@
 import android.content.res.XmlResourceParser;
 import android.text.TextUtils;
 
-import com.android.internal.pm.pkg.component.ParsedApexSystemService;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
similarity index 91%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
index 62b9947..e3bfb38 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.annotation.StringRes;
@@ -22,7 +22,6 @@
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedAttribution;
 import com.android.internal.util.DataClass;
 
 import java.util.ArrayList;
@@ -60,7 +59,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -207,10 +206,10 @@
     };
 
     @DataClass.Generated(
-            time = 1641431950829L,
+            time = 1701338881658L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java",
-            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java",
+            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java
similarity index 97%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java
index 411220a..ee5c320 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -26,7 +26,6 @@
 import android.util.ArraySet;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedAttribution;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java
similarity index 82%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java
index 512e5c7..7ee22f3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString;
 
 import static java.util.Collections.emptyMap;
 
@@ -32,12 +32,10 @@
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedComponent;
-import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -200,7 +198,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -306,10 +304,10 @@
     }
 
     @DataClass.Generated(
-            time = 1641414207885L,
+            time = 1701445673589L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java",
-            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\n  void addIntent(android.content.pm.parsing.component.ParsedIntentInfoImpl)\n  void addProperty(android.content.pm.PackageManager.Property)\npublic  android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java",
+            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<com.android.internal.pm.pkg.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic  void addIntent(com.android.internal.pm.pkg.component.ParsedIntentInfoImpl)\npublic  void addProperty(android.content.pm.PackageManager.Property)\npublic  com.android.internal.pm.pkg.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.internal.pm.pkg.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java
index 9322cf0..9e2548b 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
@@ -32,8 +32,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 /** @hide */
 class ParsedComponentUtils {
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java
similarity index 82%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java
index 7bfad14..07322e9 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -26,7 +26,6 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
@@ -112,7 +111,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -166,10 +165,10 @@
     }
 
     @DataClass.Generated(
-            time = 1641431951575L,
+            time = 1701445763455L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedInstrumentationImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java
index a711694..661c8b4 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
 import android.content.pm.parsing.result.ParseInput;
@@ -26,7 +26,6 @@
 import android.content.res.XmlResourceParser;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java
similarity index 89%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java
index ab94043..adb49e9 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -23,7 +23,6 @@
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.util.DataClass;
 
 /**
@@ -54,7 +53,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -170,10 +169,10 @@
     };
 
     @DataClass.Generated(
-            time = 1641431952314L,
+            time = 1701445800363L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java",
-            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java",
+            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
index e5e214d..c6683cf 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
 
 import android.annotation.NonNull;
 import android.content.Intent;
@@ -31,10 +31,9 @@
 import android.util.TypedValue;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
similarity index 85%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
index f322eef..bb8f565 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -25,7 +25,6 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
@@ -133,7 +132,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -227,10 +226,10 @@
     }
 
     @DataClass.Generated(
-            time = 1641414540422L,
+            time = 1701447884766L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedMainComponentImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java
index 8268f0f..7e56180 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,10 +31,8 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedIntentInfo;
-import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java
similarity index 85%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java
index afe37bc..3622019 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
 import com.android.internal.util.DataClass;
 
 /**
@@ -75,7 +74,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -172,10 +171,10 @@
     };
 
     @DataClass.Generated(
-            time = 1642132854167L,
+            time = 1701445837884L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java",
-            inputSignatures = "private  int requestDetailRes\nprivate  int backgroundRequestRes\nprivate  int backgroundRequestDetailRes\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java",
+            inputSignatures = "private  int requestDetailRes\nprivate  int backgroundRequestRes\nprivate  int backgroundRequestDetailRes\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java
similarity index 77%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java
index 69e33c8..4dcce131 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -24,8 +24,6 @@
 import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedPermission;
-import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
@@ -148,7 +146,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -162,7 +160,7 @@
             int requestRes,
             int protectionLevel,
             boolean tree,
-            @Nullable ParsedPermissionGroupImpl parsedPermissionGroup,
+            @Nullable ParsedPermissionGroup parsedPermissionGroup,
             @Nullable Set<String> knownCerts) {
         this.backgroundPermission = backgroundPermission;
         this.group = group;
@@ -237,10 +235,10 @@
     }
 
     @DataClass.Generated(
-            time = 1641414649731L,
+            time = 1701445829812L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java",
-            inputSignatures = "private static  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroupImpl parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic  android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java",
+            inputSignatures = "private static final  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable com.android.internal.pm.pkg.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedPermissionImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic  com.android.internal.pm.pkg.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java
similarity index 97%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java
index 4b45d37..5651c1c 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
 import android.content.pm.PermissionInfo;
@@ -31,10 +31,8 @@
 import android.util.Slog;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedPermission;
-import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
similarity index 93%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
index 40e3670..212fb86 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import static java.util.Collections.emptySet;
 
@@ -25,7 +25,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import com.android.internal.pm.pkg.component.ParsedProcess;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
@@ -98,7 +97,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -304,10 +303,10 @@
     };
 
     @DataClass.Generated(
-            time = 1641431953775L,
+            time = 1701445656489L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(com.android.internal.pm.pkg.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java
similarity index 97%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java
index a849549..3b2056e 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
@@ -27,11 +27,10 @@
 import android.util.ArraySet;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedProcess;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.XmlUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
similarity index 90%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
index 81a3c17..987fd41 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,7 +28,6 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
@@ -302,7 +301,7 @@
             time = 1642560323360L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic  com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
similarity index 97%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
index 0b28a12..5d82d04 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
-import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
+import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag;
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,9 +34,8 @@
 import android.util.Slog;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java
similarity index 80%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java
index ca8c45d..f4662d8 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString;
+import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -26,8 +26,6 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedMainComponent;
-import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
@@ -107,7 +105,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -141,10 +139,10 @@
     }
 
     @DataClass.Generated(
-            time = 1641431954479L,
+            time = 1701445638370L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java",
-            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java",
+            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedServiceImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
index 171ef59..a1dd19a3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
-import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
+import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,9 +32,8 @@
 import android.os.Build;
 
 import com.android.internal.R;
-import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java
similarity index 87%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java
index 78377a8..fd131df 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
 
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.pkg.component.ParsedUsesPermission;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 
@@ -51,7 +50,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -158,10 +157,10 @@
     };
 
     @DataClass.Generated(
-            time = 1641431955242L,
+            time = 1701445626268L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java",
-            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.pm.pkg.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
similarity index 97%
rename from services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
rename to core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index aa0fb27..dbe4fba 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.parsing;
+package com.android.internal.pm.pkg.parsing;
 
 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -30,7 +30,7 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
+import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
 
 import android.annotation.AnyRes;
 import android.annotation.CheckResult;
@@ -57,7 +57,6 @@
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
 import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
@@ -90,43 +89,40 @@
 import com.android.internal.R;
 import com.android.internal.os.ClassLoaderFactory;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.permission.CompatibilityPermissionInfo;
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
+import com.android.internal.pm.pkg.component.ComponentParseUtils;
+import com.android.internal.pm.pkg.component.InstallConstraintsTagParser;
 import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedActivityImpl;
+import com.android.internal.pm.pkg.component.ParsedActivityUtils;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedApexSystemServiceUtils;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedAttributionUtils;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedInstrumentationUtils;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl;
+import com.android.internal.pm.pkg.component.ParsedIntentInfoUtils;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
 import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProcessUtils;
 import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedProviderUtils;
 import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedServiceUtils;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
-import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl;
+import com.android.internal.pm.split.DefaultSplitAssetLoader;
+import com.android.internal.pm.split.SplitAssetDependencyLoader;
+import com.android.internal.pm.split.SplitAssetLoader;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
-import com.android.server.pm.SharedUidMigration;
-import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.permission.CompatibilityPermissionInfo;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ComponentParseUtils;
-import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
-import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedActivityUtils;
-import com.android.server.pm.pkg.component.ParsedApexSystemServiceUtils;
-import com.android.server.pm.pkg.component.ParsedAttributionUtils;
-import com.android.server.pm.pkg.component.ParsedInstrumentationUtils;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfoUtils;
-import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-import com.android.server.pm.pkg.component.ParsedProcessUtils;
-import com.android.server.pm.pkg.component.ParsedProviderUtils;
-import com.android.server.pm.pkg.component.ParsedServiceUtils;
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.split.DefaultSplitAssetLoader;
-import com.android.server.pm.split.SplitAssetDependencyLoader;
-import com.android.server.pm.split.SplitAssetLoader;
 
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
@@ -267,18 +263,6 @@
     public @interface ParseFlags {}
 
     /**
-     * @see #parseDefault(ParseInput, File, int, List, boolean)
-     */
-    @NonNull
-    public static ParseResult<ParsedPackage> parseDefaultOneTime(File file,
-            @ParseFlags int parseFlags,
-            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
-            boolean collectCertificates) {
-        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
-        return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates);
-    }
-
-    /**
      * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
      * request, without caching the input object and without querying the internal system state for
      * feature support.
@@ -287,30 +271,10 @@
     public static ParseResult<ParsedPackage> parseDefault(ParseInput input, File file,
             @ParseFlags int parseFlags,
             @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
-            boolean collectCertificates) {
-        ParseResult<ParsedPackage> result;
+            boolean collectCertificates, Callback callback) {
 
         ParsingPackageUtils parser = new ParsingPackageUtils(null /*separateProcesses*/,
-                null /*displayMetrics*/, splitPermissions, new Callback() {
-            @Override
-            public boolean hasFeature(String feature) {
-                // Assume the device doesn't support anything. This will affect permission
-                // parsing and will force <uses-permission/> declarations to include all
-                // requiredNotFeature permissions and exclude all requiredFeature
-                // permissions. This mirrors the old behavior.
-                return false;
-            }
-
-            @Override
-            public ParsingPackage startParsingPackage(
-                    @NonNull String packageName,
-                    @NonNull String baseApkPath,
-                    @NonNull String path,
-                    @NonNull TypedArray manifestArray, boolean isCoreApp) {
-                return PackageImpl.forParsing(packageName, baseApkPath, path, manifestArray,
-                        isCoreApp);
-            }
-        });
+                null /*displayMetrics*/, splitPermissions, callback);
         var parseResult = parser.parsePackage(input, file, parseFlags);
         if (parseResult.isError()) {
             return input.error(parseResult);
@@ -1146,7 +1110,8 @@
             case TAG_RESTRICT_UPDATE:
                 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
             case TAG_INSTALL_CONSTRAINTS:
-                return parseInstallConstraints(input, pkg, res, parser);
+                return parseInstallConstraints(input, pkg, res, parser,
+                        mCallback.getInstallConstraintsAllowlist());
             case TAG_QUERIES:
                 return parseQueries(input, pkg, res, parser);
             default:
@@ -1172,7 +1137,7 @@
         }
 
         boolean leaving = false;
-        if (!SharedUidMigration.isDisabled()) {
+        if (PackageManager.ENABLE_SHARED_UID_MIGRATION) {
             int max = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
             leaving = (max != 0) && (max < Build.VERSION.RESOURCES_SDK_INT);
         }
@@ -1858,10 +1823,11 @@
         return input.success(pkg);
     }
 
-    private static ParseResult<ParsingPackage> parseInstallConstraints(
-            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+    private static ParseResult<ParsingPackage> parseInstallConstraints(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, Set<String> allowlist)
             throws IOException, XmlPullParserException {
-        return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser);
+        return InstallConstraintsTagParser.parseInstallConstraints(
+                input, pkg, res, parser, allowlist);
     }
 
     private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
@@ -3485,5 +3451,9 @@
         ParsingPackage startParsingPackage(@NonNull String packageName,
                 @NonNull String baseApkPath, @NonNull String path,
                 @NonNull TypedArray manifestArray, boolean isCoreApp);
+
+        @NonNull Set<String> getHiddenApiWhitelistedApps();
+
+        @NonNull Set<String> getInstallConstraintsAllowlist();
     }
 }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
rename to core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java
index 1d15955..26822c6 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.pm.pkg.parsing;
+package com.android.internal.pm.pkg.parsing;
 
-import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,10 +31,9 @@
 import android.util.Slog;
 
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
-import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl;
 import com.android.internal.util.Parcelling;
 import com.android.internal.util.XmlUtils;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
 
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java
similarity index 94%
rename from services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
rename to core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java
index 0bb969f..50c6243 100644
--- a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.pm.split;
+package com.android.internal.pm.split;
 
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
@@ -21,9 +21,9 @@
 import android.content.res.AssetManager;
 import android.os.Build;
 
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
 
 import libcore.io.IoUtils;
 
diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java
similarity index 96%
rename from services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
rename to core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java
index 56d92fb..c166cdc 100644
--- a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.pm.split;
+package com.android.internal.pm.split;
 
 import android.annotation.NonNull;
 import android.content.pm.parsing.ApkLiteParseUtils;
@@ -24,8 +24,8 @@
 import android.os.Build;
 import android.util.SparseArray;
 
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
 
 import libcore.io.IoUtils;
 
diff --git a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java b/core/java/com/android/internal/pm/split/SplitAssetLoader.java
similarity index 96%
rename from services/core/java/com/android/server/pm/split/SplitAssetLoader.java
rename to core/java/com/android/internal/pm/split/SplitAssetLoader.java
index 8450159..c7c409d 100644
--- a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java
+++ b/core/java/com/android/internal/pm/split/SplitAssetLoader.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.pm.split;
+package com.android.internal.pm.split;
 
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index f86595f..adb0c69 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -58,6 +58,7 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 
 import java.security.PublicKey;
 import java.util.List;
@@ -690,7 +691,7 @@
 
     /**
      * The names of packages to adopt ownership of permissions from, parsed under {@link
-     * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
+     * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
      *
      * @see R.styleable#AndroidManifestOriginalPackage_name
      * @hide
@@ -795,7 +796,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
-     * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}.
+     * ParsingPackageUtils#TAG_KEY_SETS}.
      *
      * @see R.styleable#AndroidManifestKeySet
      * @see R.styleable#AndroidManifestPublicKey
@@ -1266,7 +1267,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
-     * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}.
+     * ParsingPackageUtils#TAG_KEY_SETS}.
      *
      * @see R.styleable#AndroidManifestUpgradeKeySet
      * @hide
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index ba2ea88..d629f6a 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -19,15 +19,20 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertThrows;
+
 import android.content.ComponentName;
 import android.net.Uri;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.common.base.Strings;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -38,6 +43,9 @@
 public class AutomaticZenRuleTest {
     private static final String CLASS = "android.app.AutomaticZenRule";
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void testLongFields_inConstructor() {
         String longString = Strings.repeat("A", 65536);
@@ -100,6 +108,7 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
     public void testLongInputsFromParcel() {
         // Create a rule with long fields, set directly via reflection so that we can confirm that
         // a rule with too-long fields that comes in via a parcel has its fields truncated directly.
@@ -152,6 +161,60 @@
                 fromParcel.getOwner().getPackageName().length());
         assertEquals(AutomaticZenRule.MAX_STRING_LENGTH,
                 fromParcel.getOwner().getClassName().length());
-        assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length());
+        assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, fromParcel.getTriggerDescription().length());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_builderWithValidType_succeeds() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+                .setType(AutomaticZenRule.TYPE_BEDTIME)
+                .build();
+        rule.validate(); // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_builderWithoutType_succeeds() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build();
+        rule.validate(); // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_constructorWithoutType_succeeds() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule("rule", new ComponentName("pkg", "cps"),
+                new ComponentName("pkg", "activity"), Uri.parse("condition"), null,
+                NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+        rule.validate(); // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_invalidType_throws() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build();
+
+        // Set the field via reflection.
+        Field typeField = AutomaticZenRule.class.getDeclaredField("mType");
+        typeField.setAccessible(true);
+        typeField.set(rule, 100);
+
+        assertThrows(IllegalArgumentException.class, rule::validate);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void setType_invalidType_throws() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build();
+
+        assertThrows(IllegalArgumentException.class, () -> rule.setType(100));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void setTypeBuilder_invalidType_throws() {
+        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("rule", Uri.parse("uri"));
+
+        assertThrows(IllegalArgumentException.class, () -> builder.setType(100));
     }
 }
diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java
index 612562e..e94273e 100644
--- a/core/tests/coretests/src/android/service/notification/ConditionTest.java
+++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java
@@ -21,9 +21,12 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertThrows;
+
 import android.app.Flags;
 import android.net.Uri;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,6 +39,7 @@
 import org.junit.runner.RunWith;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -194,4 +198,59 @@
         Condition fromParcel = new Condition(parcel);
         assertThat(fromParcel).isEqualTo(cond);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_unspecifiedSource_succeeds() {
+        new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE);
+        // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_validSource_succeeds() {
+        new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE, Condition.SOURCE_CONTEXT);
+        // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_invalidSource_throws() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new Condition(Uri.parse("uri"), "Summary", Condition.STATE_TRUE, 1000));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_parcelWithInvalidSource_throws() {
+        Condition original = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE,
+                Condition.SOURCE_SCHEDULE);
+        Parcel parcel = Parcel.obtain();
+        original.writeToParcel(parcel, 0);
+
+        // Tweak the parcel to contain and invalid source value.
+        parcel.setDataPosition(parcel.dataPosition() - 8); // going back two int fields.
+        parcel.writeInt(100);
+        parcel.setDataPosition(0);
+
+        assertThrows(IllegalArgumentException.class, () -> new Condition(parcel));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_invalidSource_throws() throws Exception {
+        Condition condition = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE,
+                Condition.SOURCE_SCHEDULE);
+
+        Field typeField = Condition.class.getDeclaredField("source");
+
+        // Reflection on reflection (ugh) to make a final field non-final
+        Field fieldAccessFlagsField = Field.class.getDeclaredField("accessFlags");
+        fieldAccessFlagsField.setAccessible(true);
+        fieldAccessFlagsField.setInt(typeField, typeField.getModifiers() & ~Modifier.FINAL);
+
+        typeField.setInt(condition, 30);
+
+        assertThrows(IllegalArgumentException.class, condition::validate);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index c980906..8d8dc10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -49,6 +49,11 @@
             @BackEvent.SwipeEdge int swipeEdge);
 
     /**
+     * Called when the input pointers are pilfered.
+     */
+    void onPilferPointers();
+
+    /**
      * Sets whether the back gesture is past the trigger threshold or not.
      */
     void setTriggerBack(boolean triggerBack);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index cf858dc..d8c691b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -111,6 +111,7 @@
 
     /** Tracks if we should start the back gesture on the next motion move event */
     private boolean mShouldStartOnNextMoveEvent = false;
+    private boolean mOnBackStartDispatched = false;
 
     private final FlingAnimationUtils mFlingAnimationUtils;
 
@@ -304,6 +305,11 @@
         }
 
         @Override
+        public void onPilferPointers() {
+            BackAnimationController.this.onPilferPointers();
+        }
+
+        @Override
         public void setTriggerBack(boolean triggerBack) {
             mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
         }
@@ -384,6 +390,16 @@
         return null;
     }
 
+    @VisibleForTesting
+    void onPilferPointers() {
+        mCurrentTracker.updateStartLocation();
+        // Dispatch onBackStarted, only to app callbacks.
+        // System callbacks will receive onBackStarted when the remote animation starts.
+        if (!shouldDispatchToAnimator()) {
+            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+        }
+    }
+
     /**
      * Called when a new motion event needs to be transferred to this
      * {@link BackAnimationController}
@@ -483,12 +499,15 @@
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
             // App is handling back animation. Cancel system animation latency tracking.
             cancelLatencyTracking();
-            dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
         }
     }
 
     private void onMove() {
-        if (!mBackGestureStarted || mBackNavigationInfo == null || mActiveCallback == null) {
+        if (!mBackGestureStarted
+                || mBackNavigationInfo == null
+                || mActiveCallback == null
+                || !mOnBackStartDispatched) {
             return;
         }
         // Skip dispatching if the move corresponds to the queued instead of the current gesture
@@ -524,13 +543,14 @@
                 && mBackNavigationInfo.isPrepareRemoteAnimation();
     }
 
-    private void dispatchOnBackStarted(IOnBackInvokedCallback callback,
+    private void tryDispatchOnBackStarted(IOnBackInvokedCallback callback,
             BackMotionEvent backEvent) {
-        if (callback == null) {
+        if (callback == null || mOnBackStartDispatched) {
             return;
         }
         try {
             callback.onBackStarted(backEvent);
+            mOnBackStartDispatched = true;
         } catch (RemoteException e) {
             Log.e(TAG, "dispatchOnBackStarted error: ", e);
         }
@@ -828,6 +848,8 @@
     void finishBackNavigation(boolean triggerBack) {
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
         mActiveCallback = null;
+        mShouldStartOnNextMoveEvent = false;
+        mOnBackStartDispatched = false;
         mShellBackAnimationRegistry.resetDefaultCrossActivity();
         cancelLatencyTracking();
         if (mBackNavigationInfo != null) {
@@ -909,7 +931,8 @@
                                                                     ::onBackAnimationFinished));
 
                                     if (apps.length >= 1) {
-                                        dispatchOnBackStarted(
+                                        mCurrentTracker.updateStartLocation();
+                                        tryDispatchOnBackStarted(
                                                 mActiveCallback,
                                                 mCurrentTracker.createStartEvent(apps[0]));
                                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 19eb928..4bd56d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -104,6 +104,15 @@
         mStartThresholdX = mInitTouchX;
     }
 
+    /** Update the start location used to compute the progress
+     * to the latest touch location.
+     */
+    void updateStartLocation() {
+        mInitTouchX = mLatestTouchX;
+        mInitTouchY = mLatestTouchY;
+        mStartThresholdX = mInitTouchX;
+    }
+
     void reset() {
         mInitTouchX = 0;
         mInitTouchY = 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index f5c01d0..4c47737 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -769,7 +769,6 @@
                         getSurfaceTransactionHelper().crop(tx, leash, destBounds);
                     }
                     if (mContentOverlay != null) {
-                        mContentOverlay.onAnimationEnd(tx, destBounds);
                         clearContentOverlay();
                     }
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index a2bd47c..e11e859 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -67,15 +67,6 @@
     public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
             Rect currentBounds, float fraction);
 
-    /**
-     * Callback when reaches the end of animation on the internal {@link #mLeash}.
-     * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
-     *                 call apply on this transaction, it should be applied on the caller side.
-     * @param destinationBounds {@link Rect} of the final bounds.
-     */
-    public abstract void onAnimationEnd(SurfaceControl.Transaction atomicTx,
-            Rect destinationBounds);
-
     /** A {@link PipContentOverlay} uses solid color. */
     public static final class PipColorOverlay extends PipContentOverlay {
         private static final String TAG = PipColorOverlay.class.getSimpleName();
@@ -107,11 +98,6 @@
             atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
         }
 
-        @Override
-        public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
-            // Do nothing. Color overlay should be fully opaque by now, ready for fade out.
-        }
-
         private float[] getContentOverlayColor(Context context) {
             final TypedArray ta = context.obtainStyledAttributes(new int[] {
                     android.R.attr.colorBackground });
@@ -164,11 +150,6 @@
                 Rect currentBounds, float fraction) {
             // Do nothing. Keep the snapshot till animation ends.
         }
-
-        @Override
-        public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
-            // Do nothing. Snapshot overlay should be fully opaque by now, ready for fade out.
-        }
     }
 
     /** A {@link PipContentOverlay} shows app icon on solid color background. */
@@ -255,11 +236,6 @@
         }
 
         @Override
-        public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
-            // Do nothing. Icon overlay should be fully opaque by now, ready for fade out.
-        }
-
-        @Override
         public void detach(SurfaceControl.Transaction tx) {
             super.detach(tx);
             if (mBitmap != null && !mBitmap.isRecycled()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 743b1ea..3635165 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -329,15 +329,7 @@
     private @Surface.Rotation int mCurrentRotation;
 
     /**
-     * An optional overlay used to mask content changing between an app in/out of PiP, only set if
-     * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true, only in gesture nav.
-     */
-    @Nullable
-    SurfaceControl mSwipePipToHomeOverlay;
-
-    /**
-     * An optional overlay used to mask content changing between an app in/out of PiP, only set if
-     * {@link PipTransitionState#getInSwipePipToHomeTransition()} is false.
+     * An optional overlay used to mask content changing between an app in/out of PiP.
      */
     @Nullable
     SurfaceControl mPipOverlay;
@@ -480,7 +472,7 @@
             return;
         }
         mPipBoundsState.setBounds(destinationBounds);
-        mSwipePipToHomeOverlay = overlay;
+        mPipOverlay = overlay;
         if (ENABLE_SHELL_TRANSITIONS && overlay != null) {
             // With Shell transition, the overlay was attached to the remote transition leash, which
             // will be removed when the current transition is finished, so we need to reparent it
@@ -892,7 +884,7 @@
         }
 
         final Rect destinationBounds = mPipBoundsState.getBounds();
-        final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay;
+        final SurfaceControl swipeToHomeOverlay = mPipOverlay;
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .resetScale(tx, mLeash, destinationBounds)
@@ -911,7 +903,7 @@
             }
         }, tx);
         mPipTransitionState.setInSwipePipToHomeTransition(false);
-        mSwipePipToHomeOverlay = null;
+        mPipOverlay = null;
     }
 
     private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable,
@@ -1126,9 +1118,9 @@
         }
 
         clearWaitForFixedRotation();
-        if (mSwipePipToHomeOverlay != null) {
-            removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */);
-            mSwipePipToHomeOverlay = null;
+        if (mPipOverlay != null) {
+            removeContentOverlay(mPipOverlay, null /* callback */);
+            mPipOverlay = null;
         }
         resetShadowRadius();
         mPipTransitionState.setInSwipePipToHomeTransition(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 0f3c162..f5f15d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -465,7 +465,7 @@
                     mSurfaceTransactionHelper.crop(tx, leash, destinationBounds)
                             .resetScale(tx, leash, destinationBounds)
                             .round(tx, leash, true /* applyCornerRadius */);
-                    if (mPipOrganizer.mSwipePipToHomeOverlay != null && !mInitBounds.isEmpty()) {
+                    if (mPipOrganizer.mPipOverlay != null && !mInitBounds.isEmpty()) {
                         // Resetting the scale for pinned task while re-adjusting its crop,
                         // also scales the overlay. So we need to update the overlay leash too.
                         Rect overlayBounds = new Rect(destinationBounds);
@@ -476,7 +476,7 @@
                                 (destinationBounds.width() - overlaySize) / 2,
                                 (destinationBounds.height() - overlaySize) / 2);
                         mSurfaceTransactionHelper.resetScale(tx,
-                                mPipOrganizer.mSwipePipToHomeOverlay, overlayBounds);
+                                mPipOrganizer.mPipOverlay, overlayBounds);
                     }
                 }
                 mInitBounds.setEmpty();
@@ -615,9 +615,9 @@
             }
         }
         // if overlay is present remove it immediately, as exit transition came before it faded out
-        if (mPipOrganizer.mSwipePipToHomeOverlay != null) {
-            startTransaction.remove(mPipOrganizer.mSwipePipToHomeOverlay);
-            clearSwipePipToHomeOverlay();
+        if (mPipOrganizer.mPipOverlay != null) {
+            startTransaction.remove(mPipOrganizer.mPipOverlay);
+            clearPipOverlay();
         }
         if (pipChange == null) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -1077,7 +1077,7 @@
         if (mFixedRotationState == FIXED_ROTATION_CALLBACK && appBounds != null) {
             mInitBounds.set(appBounds);
         }
-        final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay;
+        final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mPipOverlay;
         if (swipePipToHomeOverlay != null) {
             // Launcher fade in the overlay on top of the fullscreen Task. It is possible we
             // reparent the PIP activity to a new PIP task (in case there are other activities
@@ -1106,7 +1106,7 @@
         sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
         if (swipePipToHomeOverlay != null) {
             mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay,
-                    this::clearSwipePipToHomeOverlay /* callback */, false /* withStartDelay */);
+                    this::clearPipOverlay /* callback */, false /* withStartDelay */);
         }
         mPipTransitionState.setInSwipePipToHomeTransition(false);
     }
@@ -1250,8 +1250,8 @@
         mPipMenuController.updateMenuBounds(destinationBounds);
     }
 
-    private void clearSwipePipToHomeOverlay() {
-        mPipOrganizer.mSwipePipToHomeOverlay = null;
+    private void clearPipOverlay() {
+        mPipOrganizer.mPipOverlay = null;
     }
 
     @Override
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 0367ba1..d023cea 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
@@ -995,16 +995,10 @@
                     t.show(mOpeningTasks.get(i).mTaskSurface);
                 }
                 for (int i = 0; i < mPausingTasks.size(); ++i) {
-                    if (!sendUserLeaveHint && mPausingTasks.get(i).isLeaf()) {
-                        // This means recents is not *actually* finishing, so of course we gotta
-                        // do special stuff in WMCore to accommodate.
-                        wct.setDoNotPip(mPausingTasks.get(i).mToken);
-                    }
-                    // Since we will reparent out of the leashes, pre-emptively hide the child
-                    // surface to match the leash. Otherwise, there will be a flicker before the
-                    // visibility gets committed in Core when using split-screen (in splitscreen,
-                    // the leaf-tasks are not "independent" so aren't hidden by normal setup).
-                    t.hide(mPausingTasks.get(i).mTaskSurface);
+                    cleanUpPausingOrClosingTask(mPausingTasks.get(i), wct, t, sendUserLeaveHint);
+                }
+                for (int i = 0; i < mClosingTasks.size(); ++i) {
+                    cleanUpPausingOrClosingTask(mClosingTasks.get(i), wct, t, sendUserLeaveHint);
                 }
                 if (mPipTransaction != null && sendUserLeaveHint) {
                     SurfaceControl pipLeash = null;
@@ -1053,6 +1047,20 @@
             }
         }
 
+        private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct,
+                SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) {
+            if (!sendUserLeaveHint && task.isLeaf()) {
+                // This means recents is not *actually* finishing, so of course we gotta
+                // do special stuff in WMCore to accommodate.
+                wct.setDoNotPip(task.mToken);
+            }
+            // Since we will reparent out of the leashes, pre-emptively hide the child
+            // surface to match the leash. Otherwise, there will be a flicker before the
+            // visibility gets committed in Core when using split-screen (in splitscreen,
+            // the leaf-tasks are not "independent" so aren't hidden by normal setup).
+            finishTransaction.hide(task.mTaskSurface);
+        }
+
         @Override
         public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
         }
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 77427d9..96e57e7 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
@@ -223,6 +223,7 @@
     private boolean mExitSplitScreenOnHide;
     private boolean mIsDividerRemoteAnimating;
     private boolean mIsDropEntering;
+    private boolean mSkipEvictingMainStageChildren;
     private boolean mIsExiting;
     private boolean mIsRootTranslucent;
     @VisibleForTesting
@@ -468,6 +469,7 @@
         }
         // Due to drag already pip task entering split by this method so need to reset flag here.
         mIsDropEntering = false;
+        mSkipEvictingMainStageChildren = false;
         return true;
     }
 
@@ -572,6 +574,15 @@
             return;
         }
 
+        // Don't evict the main stage children as this can race and happen after the activity is
+        // started into that stage
+        if (!isSplitScreenVisible()) {
+            mSkipEvictingMainStageChildren = true;
+            // Starting the split task without evicting children will bring the single root task
+            // container forward, so ensure that we hide the divider before we start animate it
+            setDividerVisibility(false, null);
+        }
+
         // If split screen is not activated, we're expecting to open a pair of apps to split.
         final int extraTransitType = mMainStage.isActive()
                 ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -600,6 +611,15 @@
             return;
         }
 
+        // Don't evict the main stage children as this can race and happen after the activity is
+        // started into that stage
+        if (!isSplitScreenVisible()) {
+            mSkipEvictingMainStageChildren = true;
+            // Starting the split task without evicting children will bring the single root task
+            // container forward, so ensure that we hide the divider before we start animate it
+            setDividerVisibility(false, null);
+        }
+
         // If split screen is not activated, we're expecting to open a pair of apps to split.
         final int extraTransitType = mMainStage.isActive()
                 ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -1618,7 +1638,7 @@
             // Ensure to evict old splitting tasks because the new split pair might be composed by
             // one of the splitting tasks, evicting the task when finishing entering transition
             // won't guarantee to put the task to the indicated new position.
-            if (!mIsDropEntering) {
+            if (!mSkipEvictingMainStageChildren) {
                 mMainStage.evictAllChildren(wct);
             }
             mMainStage.reparentTopTask(wct);
@@ -1680,6 +1700,7 @@
         finishT.show(mRootTaskLeash);
         setSplitsVisible(true);
         mIsDropEntering = false;
+        mSkipEvictingMainStageChildren = false;
         mSplitRequest = null;
         updateRecentTasksSplitPair();
         if (!mLogger.hasStartedSession()) {
@@ -1929,6 +1950,7 @@
                 if (mIsDropEntering) {
                     updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
                     mIsDropEntering = false;
+                    mSkipEvictingMainStageChildren = false;
                 } else {
                     mShowDecorImmediately = true;
                     mSplitLayout.flingDividerToCenter();
@@ -2123,6 +2145,7 @@
                 if (mIsDropEntering) {
                     updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
                     mIsDropEntering = false;
+                    mSkipEvictingMainStageChildren = false;
                 } else {
                     mShowDecorImmediately = true;
                     mSplitLayout.flingDividerToCenter();
@@ -3245,6 +3268,7 @@
     public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
         if (!isSplitScreenVisible()) {
             mIsDropEntering = true;
+            mSkipEvictingMainStageChildren = true;
         }
         if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
             // If split running background, exit split first.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index e03f825..34c015f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -330,6 +330,11 @@
                     continue;
                 }
                 if (isHide) {
+                    if (pending.mType == TRANSIT_TO_BACK) {
+                        // TO_BACK is only used when setting the task view visibility immediately,
+                        // so in that case we can also hide the surface immediately
+                        startTransaction.hide(chg.getLeash());
+                    }
                     tv.prepareHideAnimation(finishTransaction);
                 } else {
                     tv.prepareCloseAnimation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 20ff79f..98d343b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
 
@@ -245,23 +246,29 @@
     public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) {
         // Unfold animation won't play when animations are disabled
         if (!ValueAnimator.areAnimatorsEnabled()) return false;
+        // Only handle transitions that are marked as physical display switch
+        // See PhysicalDisplaySwitchTransitionLauncher for the conditions
+        if ((transitionInfo.getFlags() & TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH) == 0) return false;
 
         for (int i = 0; i < transitionInfo.getChanges().size(); i++) {
             final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
-            if ((change.getFlags() & TransitionInfo.FLAG_IS_DISPLAY) != 0) {
-                if (change.getEndAbsBounds() == null || change.getStartAbsBounds() == null) {
-                    continue;
-                }
+            // We are interested only in display container changes
+            if ((change.getFlags() & TransitionInfo.FLAG_IS_DISPLAY) == 0) {
+                continue;
+            }
 
-                // Handle only unfolding, currently we don't have an animation when folding
-                final int afterArea =
-                        change.getEndAbsBounds().width() * change.getEndAbsBounds().height();
-                final int beforeArea = change.getStartAbsBounds().width()
-                        * change.getStartAbsBounds().height();
+            // Handle only unfolding, currently we don't have an animation when folding
+            if (change.getEndAbsBounds() == null || change.getStartAbsBounds() == null) {
+                continue;
+            }
 
-                if (afterArea > beforeArea) {
-                    return true;
-                }
+            final int afterArea =
+                    change.getEndAbsBounds().width() * change.getEndAbsBounds().height();
+            final int beforeArea = change.getStartAbsBounds().width()
+                    * change.getStartAbsBounds().height();
+
+            if (afterArea > beforeArea) {
+                return true;
             }
         }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 0395a9b..771876f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -182,8 +182,7 @@
     }
 
     private void triggerBackGesture() {
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 0);
+        doStartEvents(0, 0);
         mController.setTriggerBack(true);
     }
 
@@ -244,10 +243,7 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-
-        // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
 
@@ -270,10 +266,8 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ true);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-
         // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
 
@@ -359,8 +353,7 @@
                 .injectInputEvent(any(KeyEvent.class), any(Integer.class));
 
         // Verify that we start accepting gestures again once transition finishes.
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
@@ -399,8 +392,7 @@
                 .injectInputEvent(any(KeyEvent.class), any(Integer.class));
 
         // Verify that we start accepting gestures again once transition finishes.
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
@@ -427,8 +419,7 @@
         mShellExecutor.flushAll();
         reset(mAnimatorCallback);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
     }
@@ -441,9 +432,7 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
@@ -563,10 +552,8 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-
         // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
 
@@ -593,6 +580,15 @@
                 /* swipeEdge */ BackEvent.EDGE_LEFT);
     }
 
+    /**
+     * Simulate event sequence that starts a back navigation.
+     */
+    private void doStartEvents(int startX, int moveX) {
+        doMotionEvent(MotionEvent.ACTION_DOWN, startX);
+        mController.onPilferPointers();
+        doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+    }
+
     private void simulateRemoteAnimationStart() throws RemoteException {
         RemoteAnimationTarget animationTarget = createAnimationTarget();
         RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index fc1fe1c..c5e229f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -18,12 +18,11 @@
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -40,22 +39,17 @@
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
-import com.android.window.flags.FakeFeatureFlagsImpl;
-import com.android.window.flags.FeatureFlags;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.transition.TransitionInfoBuilder;
+import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
 import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
 import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -196,6 +190,22 @@
     }
 
     @Test
+    public void startAnimation_differentTransitionFromRequestWithResize_doesNotStartAnimation() {
+        mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+        boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                createDisplayResizeTransitionInfo(),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+
+        assertThat(animationStarted).isFalse();
+    }
+
+    @Test
     public void startAnimation_differentTransitionFromRequestWithoutUnfold_doesNotStart() {
         mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
         TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
@@ -403,24 +413,18 @@
         }
     }
 
-    static class TestCase {
-        private final boolean mShouldHandleMixedUnfold;
-
-        public TestCase(boolean shouldHandleMixedUnfold) {
-            mShouldHandleMixedUnfold = shouldHandleMixedUnfold;
-        }
-
-        public boolean mixedUnfoldFlagEnabled() {
-            return mShouldHandleMixedUnfold;
-        }
-
-        @Override
-        public String toString() {
-            return "shouldHandleMixedUnfold flag = " + mShouldHandleMixedUnfold;
-        }
+    private TransitionInfo createUnfoldTransitionInfo() {
+        TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
+        TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
+        change.setStartAbsBounds(new Rect(0, 0, 10, 10));
+        change.setEndAbsBounds(new Rect(0, 0, 100, 100));
+        change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
+        transitionInfo.addChange(change);
+        transitionInfo.setFlags(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH);
+        return transitionInfo;
     }
 
-    private TransitionInfo createUnfoldTransitionInfo() {
+    private TransitionInfo createDisplayResizeTransitionInfo() {
         TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
         TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
         change.setStartAbsBounds(new Rect(0, 0, 10, 10));
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 901ea46..a8ffd2b 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -110,6 +110,10 @@
     void pauseRecording(in IBinder sessionToken, in Bundle params, int userId);
     void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
 
+    // For playback control
+    void startPlayback(in IBinder sessionToken, int userId);
+    void stopPlayback(in IBinder sessionToken, int mode, int userId);
+
     // For broadcast info
     void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId);
     void removeBroadcastInfo(in IBinder sessionToken, int id, int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 5246f5c4..e37ee6e 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -63,6 +63,9 @@
     void timeShiftSetMode(int mode);
     void timeShiftEnablePositionTracking(boolean enable);
 
+    void startPlayback();
+    void stopPlayback(int mode);
+
     // For the recording session
     void startRecording(in Uri programUri, in Bundle params);
     void stopRecording();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index d749b91..ae3ee65 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -79,6 +79,8 @@
     private static final int DO_TIME_SHIFT_SET_MODE = 30;
     private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
     private static final int DO_NOTIFY_TV_MESSAGE = 32;
+    private static final int DO_STOP_PLAYBACK = 33;
+    private static final int DO_START_PLAYBACK = 34;
 
     private final boolean mIsRecordingSession;
     private final HandlerCaller mCaller;
@@ -286,6 +288,14 @@
                 mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2);
                 break;
             }
+            case DO_STOP_PLAYBACK: {
+                mTvInputSessionImpl.stopPlayback(msg.arg1);
+                break;
+            }
+            case DO_START_PLAYBACK: {
+                mTvInputSessionImpl.startPlayback();
+                break;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 break;
@@ -483,6 +493,17 @@
                 enabled));
     }
 
+    @Override
+    public void stopPlayback(int mode) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode));
+    }
+
+    @Override
+    public void startPlayback() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK));
+    }
+
+
     private final class TvInputEventReceiver extends InputEventReceiver {
         TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 631ab9a..c685a5a 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -339,6 +339,14 @@
      */
     public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END;
 
+    /**
+     * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
+     * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
+     * it has been stopped by stopPlayback.
+     * @hide
+     */
+    public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED,
@@ -3302,6 +3310,30 @@
             }
         }
 
+        void stopPlayback(int mode) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.stopPlayback(mToken, mode, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void startPlayback() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.startPlayback(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         /**
          * Sends TV messages to the service for testing purposes
          */
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 720d9a6..55fa517 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -34,6 +34,7 @@
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.media.AudioPresentation;
 import android.media.PlaybackParams;
+import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -1531,6 +1532,32 @@
         }
 
         /**
+         * Called when the application requests playback of the Audio, Video, and CC streams to be
+         * stopped, but the metadata should continue to be filtered.
+         *
+         * <p>The metadata that will continue to be filtered includes the PSI
+         * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
+         *
+         * <p> Note that this is different form {@link #timeShiftPause()} as should release the
+         * stream, making it impossible to resume from this position again.
+         * @param mode
+         * @hide
+         */
+        public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
+        }
+
+        /**
+         * Starts playback of the Audio, Video, and CC streams.
+         *
+         * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
+         * used after stopping playback. This is used to restart playback from the current position
+         * in the live broadcast.
+         * @hide
+         */
+        public void onStartPlayback() {
+        }
+
+        /**
          * Called when the application requests to play a given recorded TV program.
          *
          * @param recordedProgramUri The URI of a recorded TV program.
@@ -1993,6 +2020,20 @@
         }
 
         /**
+         * Calls {@link #onStopPlayback(int)}.
+         */
+        void stopPlayback(int mode) {
+            onStopPlayback(mode);
+        }
+
+        /**
+         * Calls {@link #onStartPlayback()}.
+         */
+        void startPlayback() {
+            onStartPlayback();
+        }
+
+        /**
          * Calls {@link #onTimeShiftPlay(Uri)}.
          */
         void timeShiftPlay(Uri recordedProgramUri) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 196b5c3..233f966 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -37,6 +37,7 @@
 import android.media.tv.TvInputManager.Session;
 import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
 import android.media.tv.TvInputManager.SessionCallback;
+import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -643,6 +644,35 @@
         }
     }
 
+    /**
+     * Stops playback of the Audio, Video, and CC streams, but continue filtering the metadata.
+     *
+     * <p>The metadata that will continue to be filtered includes the PSI
+     * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
+     *
+     * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops
+     * the stream, making it impossible to resume from this position again.
+     * @hide
+     */
+    public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
+        if (mSession != null) {
+            mSession.stopPlayback(mode);
+        }
+    }
+
+    /**
+     * Starts playback of the Audio, Video, and CC streams.
+     *
+     * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
+     * used after stopping playback. This is used to restart playback from the current position
+     * in the live broadcast.
+     * @hide
+     */
+    public void startPlayback() {
+        if (mSession != null) {
+            mSession.startPlayback();
+        }
+    }
 
     /**
      * Sends TV messages to the session for testing purposes
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 0abf285..66282dc 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -527,6 +527,15 @@
 
         final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
 
+        // No need to show permission consent dialog if it is a isSkipPrompt(true)
+        // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
+        if (mRequest.isSkipPrompt()) {
+            Log.d(TAG, "Skipping the permission consent dialog.");
+            mSingleDeviceSpinner.setVisibility(View.GONE);
+            onUserSelectedDevice(mSelectedDevice);
+            return;
+        }
+
         updatePermissionUi();
 
         mProfileIcon.setImageDrawable(profileIcon);
@@ -598,6 +607,14 @@
 
         Log.d(TAG, "onDeviceClicked(): " + mSelectedDevice.toShortString());
 
+        // No need to show permission consent dialog if it is a isSkipPrompt(true)
+        // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
+        if (mRequest.isSkipPrompt()) {
+            Log.d(TAG, "Skipping the permission consent dialog.");
+            onUserSelectedDevice(mSelectedDevice);
+            return;
+        }
+
         updatePermissionUi();
 
         mSummary.setVisibility(View.VISIBLE);
@@ -615,14 +632,6 @@
                 this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName);
         final Spanned summary;
 
-        // No need to show permission consent dialog if it is a isSkipPrompt(true)
-        // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
-        if (mRequest.isSkipPrompt()) {
-            mSingleDeviceSpinner.setVisibility(View.GONE);
-            onUserSelectedDevice(mSelectedDevice);
-            return;
-        }
-
         if (deviceProfile == null && mRequest.isSingleDevice()) {
             summary = getHtmlFromResources(this, summaryResourceId, remoteDeviceName);
             mConstraintList.setVisibility(View.GONE);
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index f216abb..66bd6f5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -28,7 +28,14 @@
 import androidx.compose.material.icons.outlined.WarningAmber
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -41,14 +48,14 @@
 import com.android.settingslib.spa.widget.card.CardButton
 import com.android.settingslib.spa.widget.card.CardModel
 import com.android.settingslib.spa.widget.card.SettingsCard
-import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
 import com.android.settingslib.spa.widget.card.SettingsCardContent
+import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 object CardPageProvider : SettingsPageProvider {
-    override val name = "CardPage"
+    override val name = "Card"
 
     override fun getTitle(arguments: Bundle?) = TITLE
 
@@ -79,10 +86,13 @@
 
     @Composable
     private fun SettingsCardWithoutIcon() {
+        var isVisible by rememberSaveable { mutableStateOf(true) }
         SettingsCard(
             CardModel(
                 title = stringResource(R.string.sample_title),
                 text = stringResource(R.string.sample_text),
+                isVisible = { isVisible },
+                onDismiss = { isVisible = false },
                 buttons = listOf(
                     CardButton(text = "Action") {},
                 ),
@@ -92,21 +102,23 @@
 
     @Composable
     fun SampleSettingsCollapsibleCard() {
-        SettingsCollapsibleCard(
-            title = "More alerts",
-            imageVector = Icons.Outlined.Error,
-            models = listOf(
+        val context = LocalContext.current
+        var isVisible0 by rememberSaveable { mutableStateOf(true) }
+        val cards = remember {
+            mutableStateListOf(
                 CardModel(
-                    title = stringResource(R.string.sample_title),
-                    text = stringResource(R.string.sample_text),
+                    title = context.getString(R.string.sample_title),
+                    text = context.getString(R.string.sample_text),
                     imageVector = Icons.Outlined.PowerOff,
+                    isVisible = { isVisible0 },
+                    onDismiss = { isVisible0 = false },
                     buttons = listOf(
                         CardButton(text = "Action") {},
                     )
                 ),
                 CardModel(
-                    title = stringResource(R.string.sample_title),
-                    text = stringResource(R.string.sample_text),
+                    title = context.getString(R.string.sample_title),
+                    text = context.getString(R.string.sample_text),
                     imageVector = Icons.Outlined.Shield,
                     buttons = listOf(
                         CardButton(text = "Action") {},
@@ -114,6 +126,11 @@
                     )
                 )
             )
+        }
+        SettingsCollapsibleCard(
+            title = "More alerts",
+            imageVector = Icons.Outlined.Error,
+            models = cards.toList()
         )
     }
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index c143390..993cb4a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -37,6 +37,7 @@
     val itemPaddingAround = 8.dp
     val itemDividerHeight = 32.dp
 
+    val iconSmall = 16.dp
     val iconLarge = 48.dp
 
     /** The size when app icon is displayed in list. */
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
index c113f43..b18a1bc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -28,5 +28,14 @@
     val title: String,
     val text: String,
     val imageVector: ImageVector? = null,
+    val isVisible: () -> Boolean = { true },
+
+    /**
+     * A dismiss button will be displayed if this is not null.
+     *
+     * And this callback will be called when user clicks the button.
+     */
+    val onDismiss: (() -> Unit)? = null,
+
     val buttons: List<CardButton> = emptyList(),
 )
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 4379278..7eec888 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -16,28 +16,35 @@
 
 package com.android.settingslib.spa.widget.card
 
+import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Close
 import androidx.compose.material.icons.outlined.WarningAmber
 import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.Card
 import androidx.compose.material3.CardDefaults
 import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import com.android.settingslib.spa.debug.UiModePreviews
 import com.android.settingslib.spa.framework.theme.SettingsDimension
@@ -87,20 +94,31 @@
 
 @Composable
 internal fun SettingsCardImpl(model: CardModel) {
-    SettingsCardContent {
-        Column(
-            modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
-            verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
-        ) {
-            CardIcon(model.imageVector)
-            SettingsTitle(model.title)
-            SettingsBody(model.text)
-            Buttons(model.buttons)
+    AnimatedVisibility(visible = model.isVisible()) {
+        SettingsCardContent {
+            Column(
+                modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+                verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
+            ) {
+                CardHeader(model.imageVector, model.onDismiss)
+                SettingsTitle(model.title)
+                SettingsBody(model.text)
+                Buttons(model.buttons)
+            }
         }
     }
 }
 
 @Composable
+fun CardHeader(imageVector: ImageVector?, onDismiss: (() -> Unit)? = null) {
+    Row(Modifier.fillMaxWidth()) {
+        CardIcon(imageVector)
+        Spacer(modifier = Modifier.weight(1f))
+        DismissButton(onDismiss)
+    }
+}
+
+@Composable
 private fun CardIcon(imageVector: ImageVector?) {
     if (imageVector != null) {
         Icon(
@@ -113,6 +131,28 @@
 }
 
 @Composable
+private fun DismissButton(onDismiss: (() -> Unit)?) {
+    if (onDismiss == null) return
+    Surface(
+        shape = CircleShape,
+        color = MaterialTheme.colorScheme.secondaryContainer,
+    ) {
+        IconButton(
+            onClick = onDismiss,
+            modifier = Modifier.size(SettingsDimension.itemIconSize)
+        ) {
+            Icon(
+                imageVector = Icons.Outlined.Close,
+                contentDescription = stringResource(
+                    androidx.compose.material3.R.string.m3c_snackbar_dismiss
+                ),
+                modifier = Modifier.size(SettingsDimension.iconSmall),
+            )
+        }
+    }
+}
+
+@Composable
 private fun Buttons(buttons: List<CardButton>) {
     if (buttons.isNotEmpty()) {
         Row(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
index bf192a1..6e36490 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -58,7 +58,7 @@
     var expanded by rememberSaveable { mutableStateOf(false) }
     SettingsCard {
         SettingsCardContent {
-            Header(title, imageVector, models.size, expanded) { expanded = it }
+            Header(title, imageVector, models.count { it.isVisible() }, expanded) { expanded = it }
         }
         AnimatedVisibility(expanded) {
             Column {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
index fd3ae49..beb9433 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -16,10 +16,18 @@
 
 package com.android.settingslib.spa.widget.card
 
+import android.content.Context
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.isNotDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
@@ -31,6 +39,8 @@
     @get:Rule
     val composeTestRule = createComposeRule()
 
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
     @Test
     fun settingsCard_titleDisplayed() {
         composeTestRule.setContent {
@@ -96,6 +106,27 @@
         assertThat(buttonClicked).isTrue()
     }
 
+    @Test
+    fun settingsCard_dismiss() {
+        composeTestRule.setContent {
+            var isVisible by remember { mutableStateOf(true) }
+            SettingsCard(
+                CardModel(
+                    title = TITLE,
+                    text = "",
+                    isVisible = { isVisible },
+                    onDismiss = { isVisible = false },
+                )
+            )
+        }
+
+        composeTestRule.onNodeWithContentDescription(
+            context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss)
+        ).performClick()
+
+        composeTestRule.onNodeWithText(TEXT).isNotDisplayed()
+    }
+
     private companion object {
         const val TITLE = "Title"
         const val TEXT = "Text"
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
index efe1c70..aba9d7be 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
@@ -16,12 +16,20 @@
 
 package com.android.settingslib.spa.widget.card
 
+import android.content.Context
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Error
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.isNotDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import org.junit.Rule
 import org.junit.Test
@@ -32,6 +40,8 @@
     @get:Rule
     val composeTestRule = createComposeRule()
 
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
     @Test
     fun settingsCollapsibleCard_titleDisplayed() {
         setContent()
@@ -62,8 +72,22 @@
         composeTestRule.onNodeWithText(CARD_TEXT).assertIsDisplayed()
     }
 
+    @Test
+    fun settingsCollapsibleCard_dismiss() {
+        setContent()
+        composeTestRule.onNodeWithText(TITLE).performClick()
+
+        composeTestRule.onNodeWithContentDescription(
+            context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss)
+        ).performClick()
+
+        composeTestRule.onNodeWithText(CARD_TEXT).isNotDisplayed()
+        composeTestRule.onNodeWithText("0").assertIsDisplayed()
+    }
+
     private fun setContent() {
         composeTestRule.setContent {
+            var isVisible by rememberSaveable { mutableStateOf(true) }
             SettingsCollapsibleCard(
                 title = TITLE,
                 imageVector = Icons.Outlined.Error,
@@ -71,6 +95,8 @@
                     CardModel(
                         title = "",
                         text = CARD_TEXT,
+                        isVisible = { isVisible },
+                        onDismiss = { isVisible = false },
                     )
                 ),
             )
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 2cb44ec..49ac0f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -28,7 +28,9 @@
 import android.bluetooth.BluetoothLeBroadcastAssistant;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothLeBroadcastSettings;
 import android.bluetooth.BluetoothLeBroadcastSubgroup;
+import android.bluetooth.BluetoothLeBroadcastSubgroupSettings;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfile.ServiceListener;
 import android.content.ContentResolver;
@@ -42,10 +44,13 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import com.android.settingslib.R;
 
+import com.google.common.collect.ImmutableList;
+
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -376,6 +381,77 @@
                 (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null);
     }
 
+    /**
+     * Start the private Broadcast for personal audio sharing or qr code sharing.
+     *
+     * <p>The broadcast will use random string for both broadcast name and subgroup program info;
+     * The broadcast will use random string for broadcast code; The broadcast will only have one
+     * subgroup due to system limitation; The subgroup language will be null.
+     *
+     * <p>If the system started the LE Broadcast, then the system calls the corresponding callback
+     * {@link BluetoothLeBroadcast.Callback}.
+     */
+    public void startPrivateBroadcast(int quality) {
+        mNewAppSourceName = "Sharing audio";
+        if (mServiceBroadcast == null) {
+            Log.d(TAG, "The BluetoothLeBroadcast is null when starting the private broadcast.");
+            return;
+        }
+        if (mServiceBroadcast.getAllBroadcastMetadata().size()
+                >= mServiceBroadcast.getMaximumNumberOfBroadcasts()) {
+            Log.d(TAG, "Skip starting the broadcast due to number limit.");
+            return;
+        }
+        String programInfo = getProgramInfo();
+        if (DEBUG) {
+            Log.d(TAG, "startBroadcast: language = null ,programInfo = " + programInfo);
+        }
+        // Current broadcast framework only support one subgroup
+        BluetoothLeBroadcastSubgroupSettings subgroupSettings =
+                buildBroadcastSubgroupSettings(/* language= */ null, programInfo, quality);
+        BluetoothLeBroadcastSettings settings =
+                buildBroadcastSettings(
+                        true, // TODO: set to false after framework fix
+                        TextUtils.isEmpty(programInfo) ? null : programInfo,
+                        (mBroadcastCode != null && mBroadcastCode.length > 0)
+                                ? mBroadcastCode
+                                : null,
+                        ImmutableList.of(subgroupSettings));
+        mServiceBroadcast.startBroadcast(settings);
+    }
+
+    private BluetoothLeBroadcastSettings buildBroadcastSettings(
+            boolean isPublic,
+            @Nullable String broadcastName,
+            @Nullable byte[] broadcastCode,
+            List<BluetoothLeBroadcastSubgroupSettings> subgroupSettingsList) {
+        BluetoothLeBroadcastSettings.Builder builder =
+                new BluetoothLeBroadcastSettings.Builder()
+                        .setPublicBroadcast(isPublic)
+                        .setBroadcastName(broadcastName)
+                        .setBroadcastCode(broadcastCode);
+        for (BluetoothLeBroadcastSubgroupSettings subgroupSettings : subgroupSettingsList) {
+            builder.addSubgroupSettings(subgroupSettings);
+        }
+        return builder.build();
+    }
+
+    private BluetoothLeBroadcastSubgroupSettings buildBroadcastSubgroupSettings(
+            @Nullable String language, @Nullable String programInfo, int quality) {
+        BluetoothLeAudioContentMetadata metadata =
+                new BluetoothLeAudioContentMetadata.Builder()
+                        .setLanguage(language)
+                        .setProgramInfo(programInfo)
+                        .build();
+        // Current broadcast framework only support one subgroup, thus we still maintain the latest
+        // metadata to keep legacy UI working.
+        mBluetoothLeAudioContentMetadata = metadata;
+        return new BluetoothLeBroadcastSubgroupSettings.Builder()
+                .setPreferredQuality(quality)
+                .setContentMetadata(mBluetoothLeAudioContentMetadata)
+                .build();
+    }
+
     public String getProgramInfo() {
         return mProgramInfo;
     }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a26b311..facb244 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -61,6 +61,13 @@
 }
 
 flag {
+    name: "refactor_get_current_user"
+    namespace: "systemui"
+    description: "KeyguardUpdateMonitor.getCurrentUser() was providing outdated results."
+    bug: "305984787"
+}
+
+flag {
     name: "notification_throttle_hun"
     namespace: "systemui"
     description: "During notification avalanche, throttle HUNs showing in fast succession."
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index e538e09..2944bd9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -174,8 +174,8 @@
     lerp: (T, T, Float) -> T,
     canOverflow: Boolean,
 ): T {
-    val state = layoutImpl.state.transitionState
-    if (state !is TransitionState.Transition || !layoutImpl.isTransitionReady(state)) {
+    val transition = layoutImpl.state.currentTransition
+    if (transition == null || !layoutImpl.isTransitionReady(transition)) {
         return sharedValue.value
     }
 
@@ -191,10 +191,11 @@
         return value as Element.SharedValue<T>
     }
 
-    val fromValue = sceneValue(state.fromScene)
-    val toValue = sceneValue(state.toScene)
+    val fromValue = sceneValue(transition.fromScene)
+    val toValue = sceneValue(transition.toScene)
     return if (fromValue != null && toValue != null) {
-        val progress = if (canOverflow) state.progress else state.progress.coerceIn(0f, 1f)
+        val progress =
+            if (canOverflow) transition.progress else transition.progress.coerceIn(0f, 1f)
         lerp(fromValue.value, toValue.value, progress)
     } else if (fromValue != null) {
         fromValue.value
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index de69c37..ba6d00e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -28,11 +28,11 @@
  * the currently running transition, if there is one.
  */
 internal fun CoroutineScope.animateToScene(
-    layoutImpl: SceneTransitionLayoutImpl,
+    layoutState: SceneTransitionLayoutStateImpl,
     target: SceneKey,
 ) {
-    val state = layoutImpl.state.transitionState
-    if (state.currentScene == target) {
+    val transitionState = layoutState.transitionState
+    if (transitionState.currentScene == target) {
         // This can happen in 3 different situations, for which there isn't anything else to do:
         //  1. There is no ongoing transition and [target] is already the current scene.
         //  2. The user is swiping to [target] from another scene and released their pointer such
@@ -44,44 +44,47 @@
         return
     }
 
-    when (state) {
-        is TransitionState.Idle -> animate(layoutImpl, target)
+    when (transitionState) {
+        is TransitionState.Idle -> animate(layoutState, target)
         is TransitionState.Transition -> {
             // A transition is currently running: first check whether `transition.toScene` or
             // `transition.fromScene` is the same as our target scene, in which case the transition
             // can be accelerated or reversed to end up in the target state.
 
-            if (state.toScene == target) {
+            if (transitionState.toScene == target) {
                 // The user is currently swiping to [target] but didn't release their pointer yet:
                 // animate the progress to `1`.
 
-                check(state.fromScene == state.currentScene)
-                val progress = state.progress
+                check(transitionState.fromScene == transitionState.currentScene)
+                val progress = transitionState.progress
                 if ((1f - progress).absoluteValue < ProgressVisibilityThreshold) {
-                    // The transition is already finished (progress ~= 1): no need to animate.
-                    layoutImpl.state.transitionState = TransitionState.Idle(state.currentScene)
+                    // The transition is already finished (progress ~= 1): no need to animate. We
+                    // finish the current transition early to make sure that the current state
+                    // change is committed.
+                    layoutState.finishTransition(transitionState, transitionState.currentScene)
                 } else {
                     // The transition is in progress: start the canned animation at the same
                     // progress as it was in.
                     // TODO(b/290184746): Also take the current velocity into account.
-                    animate(layoutImpl, target, startProgress = progress)
+                    animate(layoutState, target, startProgress = progress)
                 }
 
                 return
             }
 
-            if (state.fromScene == target) {
+            if (transitionState.fromScene == target) {
                 // There is a transition from [target] to another scene: simply animate the same
                 // transition progress to `0`.
 
-                check(state.toScene == state.currentScene)
-                val progress = state.progress
+                check(transitionState.toScene == transitionState.currentScene)
+                val progress = transitionState.progress
                 if (progress.absoluteValue < ProgressVisibilityThreshold) {
-                    // The transition is at progress ~= 0: no need to animate.
-                    layoutImpl.state.transitionState = TransitionState.Idle(state.currentScene)
+                    // The transition is at progress ~= 0: no need to animate.We finish the current
+                    // transition early to make sure that the current state change is committed.
+                    layoutState.finishTransition(transitionState, transitionState.currentScene)
                 } else {
                     // TODO(b/290184746): Also take the current velocity into account.
-                    animate(layoutImpl, target, startProgress = progress, reversed = true)
+                    animate(layoutState, target, startProgress = progress, reversed = true)
                 }
 
                 return
@@ -89,27 +92,22 @@
 
             // Generic interruption; the current transition is neither from or to [target].
             // TODO(b/290930950): Better handle interruptions here.
-            animate(layoutImpl, target)
+            animate(layoutState, target)
         }
     }
 }
 
 private fun CoroutineScope.animate(
-    layoutImpl: SceneTransitionLayoutImpl,
+    layoutState: SceneTransitionLayoutStateImpl,
     target: SceneKey,
     startProgress: Float = 0f,
     reversed: Boolean = false,
 ) {
-    val fromScene = layoutImpl.state.transitionState.currentScene
+    val fromScene = layoutState.transitionState.currentScene
     val isUserInput =
-        (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
+        (layoutState.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
             ?: false
 
-    val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
-    val visibilityThreshold =
-        (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
-    val animatable = Animatable(startProgress, visibilityThreshold = visibilityThreshold)
-
     val targetProgress = if (reversed) 0f else 1f
     val transition =
         if (reversed) {
@@ -119,7 +117,6 @@
                 currentScene = target,
                 isInitiatedByUserInput = isUserInput,
                 isUserInputOngoing = false,
-                animatable = animatable,
             )
         } else {
             OneOffTransition(
@@ -128,21 +125,27 @@
                 currentScene = target,
                 isInitiatedByUserInput = isUserInput,
                 isUserInputOngoing = false,
-                animatable = animatable,
             )
         }
 
-    // Change the current layout state to use this new transition.
-    layoutImpl.state.transitionState = transition
+    // Change the current layout state to start this new transition. This will compute the
+    // TransformationSpec associated to this transition, which we need to initialize the Animatable
+    // that will actually animate it.
+    layoutState.startTransition(transition)
+
+    // The transformation now contains the spec that we should use to instantiate the Animatable.
+    val animationSpec = layoutState.transformationSpec.progressSpec
+    val visibilityThreshold =
+        (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
+    val animatable =
+        Animatable(startProgress, visibilityThreshold = visibilityThreshold).also {
+            transition.animatable = it
+        }
 
     // Animate the progress to its target value.
     launch {
         animatable.animateTo(targetProgress, animationSpec)
-
-        // Unless some other external state change happened, the state should now be idle.
-        if (layoutImpl.state.transitionState == transition) {
-            layoutImpl.state.transitionState = TransitionState.Idle(target)
-        }
+        layoutState.finishTransition(transition, target)
     }
 }
 
@@ -152,8 +155,16 @@
     override val currentScene: SceneKey,
     override val isInitiatedByUserInput: Boolean,
     override val isUserInputOngoing: Boolean,
-    private val animatable: Animatable<Float, AnimationVector1D>,
 ) : TransitionState.Transition(fromScene, toScene) {
+    /**
+     * The animatable used to animate this transition.
+     *
+     * Note: This is lateinit because we need to first create this Transition object so that
+     * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
+     * it, which is need to initialize this Animatable.
+     */
+    lateinit var animatable: Animatable<Float, AnimationVector1D>
+
     override val progress: Float
         get() = animatable.value
 }
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 431a8ae..5dc1079 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
@@ -181,15 +181,11 @@
 }
 
 internal class ElementNode(
-    layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
-    element: Element,
-    sceneValues: Element.TargetValues,
+    private var layoutImpl: SceneTransitionLayoutImpl,
+    private var scene: Scene,
+    private var element: Element,
+    private var sceneValues: Element.TargetValues,
 ) : Modifier.Node(), DrawModifierNode {
-    private var layoutImpl: SceneTransitionLayoutImpl = layoutImpl
-    private var scene: Scene = scene
-    private var element: Element = element
-    private var sceneValues: Element.TargetValues = sceneValues
 
     override fun onAttach() {
         super.onAttach()
@@ -283,26 +279,27 @@
     scene: Scene,
     element: Element,
 ): Boolean {
-    val state = layoutImpl.state.transitionState
+    val transition = layoutImpl.state.currentTransition
 
     // Always draw the element if there is no ongoing transition or if the element is not shared.
     if (
-        state !is TransitionState.Transition ||
-            !layoutImpl.isTransitionReady(state) ||
-            state.fromScene !in element.sceneValues ||
-            state.toScene !in element.sceneValues
+        transition == null ||
+            !layoutImpl.isTransitionReady(transition) ||
+            transition.fromScene !in element.sceneValues ||
+            transition.toScene !in element.sceneValues
     ) {
         return true
     }
 
-    val sharedTransformation = sharedElementTransformation(layoutImpl, state, element.key)
+    val sharedTransformation =
+        sharedElementTransformation(layoutImpl.state, transition, element.key)
     if (sharedTransformation?.enabled == false) {
         return true
     }
 
     return shouldDrawOrComposeSharedElement(
         layoutImpl,
-        state,
+        transition,
         scene.key,
         element.key,
         sharedTransformation,
@@ -331,21 +328,21 @@
 }
 
 private fun isSharedElementEnabled(
-    layoutImpl: SceneTransitionLayoutImpl,
+    layoutState: SceneTransitionLayoutStateImpl,
     transition: TransitionState.Transition,
     element: ElementKey,
 ): Boolean {
-    return sharedElementTransformation(layoutImpl, transition, element)?.enabled ?: true
+    return sharedElementTransformation(layoutState, transition, element)?.enabled ?: true
 }
 
 internal fun sharedElementTransformation(
-    layoutImpl: SceneTransitionLayoutImpl,
+    layoutState: SceneTransitionLayoutStateImpl,
     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
+    val transformationSpec = layoutState.transformationSpec
+    val sharedInFromScene = transformationSpec.transformations(element, transition.fromScene).shared
+    val sharedInToScene = transformationSpec.transformations(element, transition.toScene).shared
 
     // The sharedElement() transformation must either be null or be the same in both scenes.
     if (sharedInFromScene != sharedInToScene) {
@@ -371,13 +368,9 @@
     scene: Scene,
     sceneValues: Element.TargetValues,
 ): Boolean {
-    val state = layoutImpl.state.transitionState
+    val transition = layoutImpl.state.currentTransition ?: return true
 
-    if (state !is TransitionState.Transition) {
-        return true
-    }
-
-    if (!layoutImpl.isTransitionReady(state)) {
+    if (!layoutImpl.isTransitionReady(transition)) {
         val lastValue =
             sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified }
                 ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f
@@ -385,8 +378,8 @@
         return lastValue == 1f
     }
 
-    val fromScene = state.fromScene
-    val toScene = state.toScene
+    val fromScene = transition.fromScene
+    val toScene = transition.toScene
     val fromValues = element.sceneValues[fromScene]
     val toValues = element.sceneValues[toScene]
 
@@ -395,14 +388,11 @@
     }
 
     val isSharedElement = fromValues != null && toValues != null
-    if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
+    if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) {
         return true
     }
 
-    return layoutImpl.transitions
-        .transitionSpec(fromScene, toScene)
-        .transformations(element.key, scene.key)
-        .alpha == null
+    return layoutImpl.state.transformationSpec.transformations(element.key, scene.key).alpha == null
 }
 
 /**
@@ -607,24 +597,22 @@
     lastValue: () -> T,
     lerp: (T, T, Float) -> T,
 ): T {
-    val state = layoutImpl.state.transitionState
-
-    // There is no ongoing transition.
-    if (state !is TransitionState.Transition) {
-        // Even if this element SceneTransitionLayout is not animated, the layout itself might be
-        // animated (e.g. by another parent SceneTransitionLayout), in which case this element still
-        // need to participate in the layout phase.
-        return currentValue()
-    }
+    val transition =
+        layoutImpl.state.currentTransition
+        // There is no ongoing transition. Even if this element SceneTransitionLayout is not
+        // animated, the layout itself might be animated (e.g. by another parent
+        // SceneTransitionLayout), in which case this element still need to participate in the
+        // layout phase.
+        ?: return currentValue()
 
     // A transition was started but it's not ready yet (not all elements have been composed/laid
     // out yet). Use the last value that was set, to make sure elements don't unexpectedly jump.
-    if (!layoutImpl.isTransitionReady(state)) {
+    if (!layoutImpl.isTransitionReady(transition)) {
         return lastValue()
     }
 
-    val fromScene = state.fromScene
-    val toScene = state.toScene
+    val fromScene = transition.fromScene
+    val toScene = transition.toScene
     val fromValues = element.sceneValues[fromScene]
     val toValues = element.sceneValues[toScene]
 
@@ -638,21 +626,17 @@
     // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
     // elements follow the finger direction.
     val isSharedElement = fromValues != null && toValues != null
-    if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
+    if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) {
         val start = sceneValue(fromValues!!)
         val end = sceneValue(toValues!!)
 
         // Make sure we don't read progress if values are the same and we don't need to interpolate,
         // so we don't invalidate the phase where this is read.
-        return if (start == end) start else lerp(start, end, state.progress)
+        return if (start == end) start else lerp(start, end, transition.progress)
     }
 
     val transformation =
-        transformation(
-            layoutImpl.transitions
-                .transitionSpec(fromScene, toScene)
-                .transformations(element.key, scene.key)
-        )
+        transformation(layoutImpl.state.transformationSpec.transformations(element.key, scene.key))
         // If there is no transformation explicitly associated to this element value, let's use
         // the value given by the system (like the current position and size given by the layout
         // pass).
@@ -675,7 +659,7 @@
             scene,
             element,
             sceneValues,
-            state,
+            transition,
             idleValue,
         )
 
@@ -685,7 +669,7 @@
         return targetValue
     }
 
-    val progress = state.progress
+    val progress = transition.progress
     // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range.
     val rangeProgress = transformation.range?.progress(progress) ?: progress
 
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 7029da2..306f276 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
@@ -120,17 +120,13 @@
     scene: SceneKey,
     element: Element,
 ): Boolean {
-    val transitionState = layoutImpl.state.transitionState
-
-    // If we are idle, there is only one [scene] that is composed so we can compose our movable
-    // content here.
-    if (transitionState is TransitionState.Idle) {
-        check(transitionState.currentScene == scene)
-        return true
-    }
-
-    val fromScene = (transitionState as TransitionState.Transition).fromScene
-    val toScene = transitionState.toScene
+    val transition =
+        layoutImpl.state.currentTransition
+        // If we are idle, there is only one [scene] that is composed so we can compose our
+        // movable content here.
+        ?: return true
+    val fromScene = transition.fromScene
+    val toScene = transition.toScene
 
     val fromReady = layoutImpl.isSceneReady(fromScene)
     val toReady = layoutImpl.isSceneReady(toScene)
@@ -181,10 +177,10 @@
 
     return shouldDrawOrComposeSharedElement(
         layoutImpl,
-        transitionState,
+        transition,
         scene,
         element.key,
-        sharedElementTransformation(layoutImpl, transitionState, element.key),
+        sharedElementTransformation(layoutImpl.state, transition, element.key),
     )
 }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 32025b4..e78f326 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -179,7 +179,8 @@
     bottomOrRightBehavior: NestedScrollBehavior,
 ) =
     SceneNestedScrollHandler(
-            gestureHandler = layoutImpl.gestureHandler(orientation = orientation),
+            layoutImpl = layoutImpl,
+            orientation = orientation,
             topOrLeftBehavior = topOrLeftBehavior,
             bottomOrRightBehavior = bottomOrRightBehavior,
         )
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 91decf4..338557d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -41,14 +41,9 @@
     internal val orientation: Orientation,
     private val coroutineScope: CoroutineScope,
 ) {
+    private val layoutState = layoutImpl.state
     val draggable: DraggableHandler = SceneDraggableHandler(this)
 
-    internal var transitionState
-        get() = layoutImpl.state.transitionState
-        set(value) {
-            layoutImpl.state.transitionState = value
-        }
-
     private var _swipeTransition: SwipeTransition? = null
     internal var swipeTransition: SwipeTransition
         get() = _swipeTransition ?: error("SwipeTransition needs to be initialized")
@@ -57,27 +52,26 @@
         }
 
     private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
-        if (isDrivingTransition || force) transitionState = newTransition
+        if (isDrivingTransition || force) layoutState.startTransition(newTransition)
         swipeTransition = newTransition
     }
 
-    internal val currentScene: Scene
-        get() = layoutImpl.scene(transitionState.currentScene)
-
     internal val isDrivingTransition
-        get() = transitionState == _swipeTransition
+        get() = layoutState.transitionState == _swipeTransition
 
     /**
      * The velocity threshold at which the intent of the user is to swipe up or down. It is the same
      * as SwipeableV2Defaults.VelocityThreshold.
      */
-    internal val velocityThreshold = with(layoutImpl.density) { 125.dp.toPx() }
+    internal val velocityThreshold: Float
+        get() = with(layoutImpl.density) { 125.dp.toPx() }
 
     /**
      * The positional threshold at which the intent of the user is to swipe to the next scene. It is
      * the same as SwipeableV2Defaults.PositionalThreshold.
      */
-    private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() }
+    private val positionalThreshold
+        get() = with(layoutImpl.density) { 56.dp.toPx() }
 
     internal var gestureWithPriority: Any? = null
 
@@ -98,18 +92,18 @@
             return
         }
 
-        val transition = transitionState
-        if (transition is TransitionState.Transition) {
+        val transitionState = layoutState.transitionState
+        if (transitionState is TransitionState.Transition) {
             // TODO(b/290184746): Better handle interruptions here if state != idle.
             Log.w(
                 TAG,
                 "start from TransitionState.Transition is not fully supported: from" +
-                    " ${transition.fromScene} to ${transition.toScene} " +
-                    "(progress ${transition.progress})"
+                    " ${transitionState.fromScene} to ${transitionState.toScene} " +
+                    "(progress ${transitionState.progress})"
             )
         }
 
-        val fromScene = currentScene
+        val fromScene = layoutImpl.scene(transitionState.currentScene)
         setCurrentActions(fromScene, startedPosition, pointersDown)
 
         val (targetScene, distance) =
@@ -364,7 +358,7 @@
                     findTargetSceneAndDistanceStrict(fromScene, velocity)
                         ?: run {
                             // We will not animate
-                            transitionState = TransitionState.Idle(fromScene.key)
+                            layoutState.finishTransition(swipeTransition, idleScene = fromScene.key)
                             return
                         }
 
@@ -439,14 +433,7 @@
                 )
 
                 swipeTransition.finishOffsetAnimation()
-
-                // Now that the animation is done, the state should be idle. Note that if the state
-                // was changed since this animation started, some external code changed it and we
-                // shouldn't do anything here. Note also that this job will be cancelled in the case
-                // where the user intercepts this swipe.
-                if (isDrivingTransition) {
-                    transitionState = TransitionState.Idle(targetScene)
-                }
+                layoutState.finishTransition(swipeTransition, targetScene)
             }
         }
     }
@@ -539,10 +526,14 @@
 }
 
 internal class SceneNestedScrollHandler(
-    private val gestureHandler: SceneGestureHandler,
+    private val layoutImpl: SceneTransitionLayoutImpl,
+    private val orientation: Orientation,
     private val topOrLeftBehavior: NestedScrollBehavior,
     private val bottomOrRightBehavior: NestedScrollBehavior,
 ) : NestedScrollHandler {
+    private val layoutState = layoutImpl.state
+    private val gestureHandler = layoutImpl.gestureHandler(orientation)
+
     override val connection: PriorityNestedScrollConnection = nestedScrollConnection()
 
     private fun nestedScrollConnection(): PriorityNestedScrollConnection {
@@ -553,7 +544,7 @@
         val actionUpOrLeft =
             Swipe(
                 direction =
-                    when (gestureHandler.orientation) {
+                    when (orientation) {
                         Orientation.Horizontal -> SwipeDirection.Left
                         Orientation.Vertical -> SwipeDirection.Up
                     },
@@ -563,7 +554,7 @@
         val actionDownOrRight =
             Swipe(
                 direction =
-                    when (gestureHandler.orientation) {
+                    when (orientation) {
                         Orientation.Horizontal -> SwipeDirection.Right
                         Orientation.Vertical -> SwipeDirection.Down
                     },
@@ -571,7 +562,7 @@
             )
 
         fun hasNextScene(amount: Float): Boolean {
-            val fromScene = gestureHandler.currentScene
+            val fromScene = layoutImpl.scene(layoutState.transitionState.currentScene)
             val nextScene =
                 when {
                     amount < 0f -> fromScene.userActions[actionUpOrLeft]
@@ -582,7 +573,7 @@
         }
 
         return PriorityNestedScrollConnection(
-            orientation = gestureHandler.orientation,
+            orientation = orientation,
             canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
                 canChangeScene = offsetBeforeStart == 0f
 
@@ -590,8 +581,9 @@
                     canChangeScene && gestureHandler.isDrivingTransition && offsetAvailable != 0f
                 if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
 
-                val progress = gestureHandler.swipeTransition.progress
-                val threshold = gestureHandler.layoutImpl.transitionInterceptionThreshold
+                val swipeTransition = gestureHandler.swipeTransition
+                val progress = swipeTransition.progress
+                val threshold = layoutImpl.transitionInterceptionThreshold
                 fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
 
                 // The transition is always between 0 and 1. If it is close to either of these
@@ -599,9 +591,8 @@
                 // The progress value can go beyond this range in the case of overscroll.
                 val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f)
                 if (shouldSnapToIdle) {
-                    gestureHandler.swipeTransition.cancelOffsetAnimation()
-                    gestureHandler.transitionState =
-                        TransitionState.Idle(gestureHandler.swipeTransition.currentScene)
+                    swipeTransition.cancelOffsetAnimation()
+                    layoutState.finishTransition(swipeTransition, swipeTransition.currentScene)
                 }
 
                 // Start only if we cannot consume this event
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 239971f..3608e37 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -19,6 +19,8 @@
 import androidx.annotation.FloatRange
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.remember
@@ -27,6 +29,7 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.platform.LocalDensity
+import kotlinx.coroutines.channels.Channel
 
 /**
  * [SceneTransitionLayout] is a container that automatically animates its content whenever
@@ -266,24 +269,45 @@
     val coroutineScope = rememberCoroutineScope()
     val layoutImpl = remember {
         SceneTransitionLayoutImpl(
+                state = state as SceneTransitionLayoutStateImpl,
                 onChangeScene = onChangeScene,
-                builder = scenes,
-                transitions = transitions,
-                state = state,
                 density = density,
                 edgeDetector = edgeDetector,
                 transitionInterceptionThreshold = transitionInterceptionThreshold,
+                builder = scenes,
                 coroutineScope = coroutineScope,
             )
             .also { onLayoutImpl?.invoke(it) }
     }
 
-    layoutImpl.onChangeScene = onChangeScene
-    layoutImpl.transitions = transitions
-    layoutImpl.density = density
-    layoutImpl.edgeDetector = edgeDetector
+    val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) }
+    SideEffect {
+        if (state != layoutImpl.state) {
+            error(
+                "This SceneTransitionLayout was bound to a different SceneTransitionLayoutState" +
+                    " that was used when creating it, which is not supported"
+            )
+        }
 
-    layoutImpl.setScenes(scenes)
-    layoutImpl.setCurrentScene(currentScene)
+        layoutImpl.onChangeScene = onChangeScene
+        (state as SceneTransitionLayoutStateImpl).transitions = transitions
+        layoutImpl.density = density
+        layoutImpl.edgeDetector = edgeDetector
+        layoutImpl.updateScenes(scenes)
+
+        state.transitions = transitions
+
+        targetSceneChannel.trySend(currentScene)
+    }
+
+    LaunchedEffect(targetSceneChannel) {
+        for (newKey in targetSceneChannel) {
+            // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame
+            // late.
+            val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey
+            animateToScene(layoutImpl.state, newKey)
+        }
+    }
+
     layoutImpl.Content(modifier)
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 00e33e2..c99c325 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -22,13 +22,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
@@ -40,36 +35,40 @@
 import androidx.compose.ui.util.fastForEach
 import com.android.compose.ui.util.lerp
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.Channel
 
 @Stable
 internal class SceneTransitionLayoutImpl(
-    onChangeScene: (SceneKey) -> Unit,
+    internal val state: SceneTransitionLayoutStateImpl,
+    internal var onChangeScene: (SceneKey) -> Unit,
+    internal var density: Density,
+    internal var edgeDetector: EdgeDetector,
+    internal var transitionInterceptionThreshold: Float,
     builder: SceneTransitionLayoutScope.() -> Unit,
-    transitions: SceneTransitions,
-    internal val state: SceneTransitionLayoutState,
-    density: Density,
-    edgeDetector: EdgeDetector,
-    transitionInterceptionThreshold: Float,
     coroutineScope: CoroutineScope,
 ) {
-    internal val scenes = SnapshotStateMap<SceneKey, Scene>()
+    internal val scenes = mutableMapOf<SceneKey, Scene>()
+
+    /**
+     * The map of [Element]s.
+     *
+     * Note that this map is *mutated* directly during composition, so it is a [SnapshotStateMap] to
+     * make sure that mutations are reverted if composition is cancelled.
+     */
     internal val elements = SnapshotStateMap<ElementKey, Element>()
 
-    /** The scenes that are "ready", i.e. they were composed and fully laid-out at least once. */
+    /**
+     * The scenes that are "ready", i.e. they were composed and fully laid-out at least once.
+     *
+     * Note that this map is *read* during composition, so it is a [SnapshotStateMap] to make sure
+     * that we recompose when modifications are made to this map.
+     */
     private val readyScenes = SnapshotStateMap<SceneKey, Boolean>()
 
-    internal var onChangeScene by mutableStateOf(onChangeScene)
-    internal var transitions by mutableStateOf(transitions)
-    internal var density: Density by mutableStateOf(density)
-    internal var edgeDetector by mutableStateOf(edgeDetector)
-    internal var transitionInterceptionThreshold by mutableStateOf(transitionInterceptionThreshold)
-
     private val horizontalGestureHandler: SceneGestureHandler
     private val verticalGestureHandler: SceneGestureHandler
 
     init {
-        setScenes(builder)
+        updateScenes(builder)
 
         // SceneGestureHandler must wait for the scenes to be initialized, in order to access the
         // current scene (required for SwipeTransition).
@@ -98,7 +97,7 @@
         return scenes[key] ?: error("Scene $key is not configured")
     }
 
-    internal fun setScenes(builder: SceneTransitionLayoutScope.() -> Unit) {
+    internal fun updateScenes(builder: SceneTransitionLayoutScope.() -> Unit) {
         // Keep a reference of the current scenes. After processing [builder], the scenes that were
         // not configured will be removed.
         val scenesToRemove = scenes.keys.toMutableSet()
@@ -141,20 +140,6 @@
     }
 
     @Composable
-    internal fun setCurrentScene(key: SceneKey) {
-        val channel = remember { Channel<SceneKey>(Channel.CONFLATED) }
-        SideEffect { channel.trySend(key) }
-        LaunchedEffect(channel) {
-            for (newKey in channel) {
-                // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame
-                // late.
-                val newKey = channel.tryReceive().getOrNull() ?: newKey
-                animateToScene(this@SceneTransitionLayoutImpl, newKey)
-            }
-        }
-    }
-
-    @Composable
     @OptIn(ExperimentalComposeUiApi::class)
     internal fun Content(modifier: Modifier) {
         Box(
@@ -171,14 +156,14 @@
 
                     val width: Int
                     val height: Int
-                    val state = state.transitionState
-                    if (state !is TransitionState.Transition) {
+                    val transition = state.currentTransition
+                    if (transition == null) {
                         width = placeable.width
                         height = placeable.height
                     } else {
                         // Interpolate the size.
-                        val fromSize = scene(state.fromScene).targetSize
-                        val toSize = scene(state.toScene).targetSize
+                        val fromSize = scene(transition.fromScene).targetSize
+                        val toSize = scene(transition.toScene).targetSize
 
                         // Optimization: make sure we don't read state.progress if fromSize ==
                         // toSize to avoid running this code every frame when the layout size does
@@ -187,7 +172,7 @@
                             width = fromSize.width
                             height = fromSize.height
                         } else {
-                            val size = lerp(fromSize, toSize, state.progress)
+                            val size = lerp(fromSize, toSize, transition.progress)
                             width = size.width.coerceAtLeast(0)
                             height = size.height.coerceAtLeast(0)
                         }
@@ -228,13 +213,12 @@
 
                             scene.Content(
                                 Modifier.drawWithContent {
-                                    when (val state = state.transitionState) {
-                                        is TransitionState.Idle -> drawContent()
-                                        is TransitionState.Transition -> {
-                                            // Don't draw scenes that are not ready yet.
-                                            if (readyScenes.containsKey(key)) {
-                                                drawContent()
-                                            }
+                                    if (state.currentTransition == null) {
+                                        drawContent()
+                                    } else {
+                                        // Don't draw scenes that are not ready yet.
+                                        if (readyScenes.containsKey(key)) {
+                                            drawContent()
                                         }
                                     }
                                 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 62372558..d1ba582 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -23,31 +23,32 @@
 
 /** The state of a [SceneTransitionLayout]. */
 @Stable
-class SceneTransitionLayoutState(initialScene: SceneKey) {
+sealed interface SceneTransitionLayoutState {
     /**
      * The current [TransitionState]. All values read here are backed by the Snapshot system.
      *
      * To observe those values outside of Compose/the Snapshot system, use
      * [SceneTransitionLayoutState.observableTransitionState] instead.
      */
-    var transitionState: TransitionState by mutableStateOf(TransitionState.Idle(initialScene))
+    val transitionState: TransitionState
+
+    /** The current transition, or `null` if we are idle. */
+    val currentTransition: TransitionState.Transition?
+        get() = transitionState as? TransitionState.Transition
 
     /**
-     * Whether we are transitioning, optionally restricting the check to the transition between
-     * [from] and [to].
+     * Whether we are transitioning. If [from] or [to] is empty, we will also check that they match
+     * the scenes we are animating from and/or to.
      */
-    fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
-        val transition = transitionState as? TransitionState.Transition ?: return false
-
-        return (from == null || transition.fromScene == from) &&
-            (to == null || transition.toScene == to)
-    }
+    fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean
 
     /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */
-    fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
-        return isTransitioning(from = scene, to = other) ||
-            isTransitioning(from = other, to = scene)
-    }
+    fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean
+}
+
+/** Create a new [SceneTransitionLayoutState] that is currently idle at scene [currentScene]. */
+fun SceneTransitionLayoutState(currentScene: SceneKey): SceneTransitionLayoutState {
+    return SceneTransitionLayoutStateImpl(currentScene, SceneTransitions.Empty)
 }
 
 @Stable
@@ -93,3 +94,50 @@
         abstract val isUserInputOngoing: Boolean
     }
 }
+
+internal class SceneTransitionLayoutStateImpl(
+    initialScene: SceneKey,
+    internal var transitions: SceneTransitions,
+) : SceneTransitionLayoutState {
+    override var transitionState: TransitionState by
+        mutableStateOf(TransitionState.Idle(initialScene))
+        private set
+
+    /**
+     * The current [transformationSpec] associated to [transitionState]. Accessing this value makes
+     * sense only if [transitionState] is a [TransitionState.Transition].
+     */
+    internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+
+    override fun isTransitioning(from: SceneKey?, to: SceneKey?): Boolean {
+        val transition = currentTransition ?: return false
+        return (from == null || transition.fromScene == from) &&
+            (to == null || transition.toScene == to)
+    }
+
+    override fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
+        return isTransitioning(from = scene, to = other) ||
+            isTransitioning(from = other, to = scene)
+    }
+
+    /** Start a new [transition], instantly interrupting any ongoing transition if there was one. */
+    internal fun startTransition(transition: TransitionState.Transition) {
+        // Compute the [TransformationSpec] when the transition starts.
+        transformationSpec =
+            transitions
+                .transitionSpec(transition.fromScene, transition.toScene)
+                .transformationSpec()
+
+        transitionState = transition
+    }
+
+    /**
+     * Notify that [transition] was finished and that we should settle to [idleScene]. This will do
+     * nothing if [transition] was interrupted since it was started.
+     */
+    internal fun finishTransition(transition: TransitionState.Transition, idleScene: SceneKey) {
+        if (transitionState == transition) {
+            transitionState = TransitionState.Idle(idleScene)
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index f91895b..3a55567 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -18,11 +18,9 @@
 
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.snap
-import androidx.compose.runtime.Stable
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
 import com.android.compose.animation.scene.transformation.AnchoredSize
 import com.android.compose.animation.scene.transformation.AnchoredTranslate
 import com.android.compose.animation.scene.transformation.DrawScale
@@ -36,16 +34,17 @@
 import com.android.compose.animation.scene.transformation.Translate
 
 /** The transitions configuration of a [SceneTransitionLayout]. */
-class SceneTransitions(
-    internal val transitionSpecs: List<TransitionSpec>,
+class SceneTransitions
+internal constructor(
+    internal val transitionSpecs: List<TransitionSpecImpl>,
 ) {
-    private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpec>>()
+    private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpecImpl>>()
 
-    internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
+    internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpecImpl {
         return cache.getOrPut(from) { mutableMapOf() }.getOrPut(to) { findSpec(from, to) }
     }
 
-    private fun findSpec(from: SceneKey, to: SceneKey): TransitionSpec {
+    private fun findSpec(from: SceneKey, to: SceneKey): TransitionSpecImpl {
         val spec = transition(from, to) { it.from == from && it.to == to }
         if (spec != null) {
             return spec
@@ -53,7 +52,7 @@
 
         val reversed = transition(from, to) { it.from == to && it.to == from }
         if (reversed != null) {
-            return reversed.reverse()
+            return reversed.reversed()
         }
 
         val relaxedSpec =
@@ -67,16 +66,16 @@
         return transition(from, to) {
                 (it.from == to && it.to == null) || (it.to == from && it.from == null)
             }
-            ?.reverse()
+            ?.reversed()
             ?: defaultTransition(from, to)
     }
 
     private fun transition(
         from: SceneKey,
         to: SceneKey,
-        filter: (TransitionSpec) -> Boolean,
-    ): TransitionSpec? {
-        var match: TransitionSpec? = null
+        filter: (TransitionSpecImpl) -> Boolean,
+    ): TransitionSpecImpl? {
+        var match: TransitionSpecImpl? = null
         transitionSpecs.fastForEach { spec ->
             if (filter(spec)) {
                 if (match != null) {
@@ -89,28 +88,88 @@
     }
 
     private fun defaultTransition(from: SceneKey, to: SceneKey) =
-        TransitionSpec(from, to, emptyList(), snap())
+        TransitionSpecImpl(from, to, TransformationSpec.EmptyProvider)
+
+    companion object {
+        val Empty = SceneTransitions(transitionSpecs = emptyList())
+    }
 }
 
 /** The definition of a transition between [from] and [to]. */
-@Stable
-data class TransitionSpec(
-    val from: SceneKey?,
-    val to: SceneKey?,
-    val transformations: List<Transformation>,
-    val spec: AnimationSpec<Float>,
-) {
-    // TODO(b/302300957): Make sure this cache does not infinitely grow.
-    private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>()
+interface TransitionSpec {
+    /**
+     * The scene we are transitioning from. If `null`, this spec can be used to animate from any
+     * scene.
+     */
+    val from: SceneKey?
 
-    internal fun reverse(): TransitionSpec {
-        return copy(
+    /**
+     * The scene we are transitioning to. If `null`, this spec can be used to animate from any
+     * scene.
+     */
+    val to: SceneKey?
+
+    /**
+     * Return a reversed version of this [TransitionSpec] for a transition going from [to] to
+     * [from].
+     */
+    fun reversed(): TransitionSpec
+
+    /*
+     * The [TransformationSpec] associated to this [TransitionSpec].
+     *
+     * Note that this is called once every a transition associated to this [TransitionSpec] is
+     * started.
+     */
+    fun transformationSpec(): TransformationSpec
+}
+
+interface TransformationSpec {
+    /** The [AnimationSpec] used to animate the associated transition progress. */
+    val progressSpec: AnimationSpec<Float>
+
+    /** The list of [Transformation] applied to elements during this transition. */
+    val transformations: List<Transformation>
+
+    companion object {
+        internal val Empty =
+            TransformationSpecImpl(progressSpec = snap(), transformations = emptyList())
+        internal val EmptyProvider = { Empty }
+    }
+}
+
+internal class TransitionSpecImpl(
+    override val from: SceneKey?,
+    override val to: SceneKey?,
+    private val transformationSpec: () -> TransformationSpecImpl,
+) : TransitionSpec {
+    override fun reversed(): TransitionSpecImpl {
+        return TransitionSpecImpl(
             from = to,
             to = from,
-            transformations = transformations.fastMap { it.reverse() },
+            transformationSpec = {
+                val reverse = transformationSpec.invoke()
+                TransformationSpecImpl(
+                    progressSpec = reverse.progressSpec,
+                    transformations = reverse.transformations.map { it.reversed() }
+                )
+            }
         )
     }
 
+    override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke()
+}
+
+/**
+ * An implementation of [TransformationSpec] that allows the quick retrieval of an element
+ * [ElementTransformations].
+ */
+internal class TransformationSpecImpl(
+    override val progressSpec: AnimationSpec<Float>,
+    override val transformations: List<Transformation>,
+) : TransformationSpec {
+    private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>()
+
     internal fun transformations(element: ElementKey, scene: SceneKey): ElementTransformations {
         return cache
             .getOrPut(element) { mutableMapOf() }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 116a666..0d3bc7d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -27,7 +27,8 @@
     fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean =
         userActions.keys.any { it is Swipe && it.direction.orientation == orientation }
 
-    val currentScene = gestureHandler.currentScene
+    val layoutImpl = gestureHandler.layoutImpl
+    val currentScene = layoutImpl.scene(layoutImpl.state.transitionState.currentScene)
     val orientation = gestureHandler.orientation
     val canSwipe = currentScene.shouldEnableSwipes(orientation)
     val canOppositeSwipe =
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 8f4a36e..7046866 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
@@ -44,7 +44,7 @@
 }
 
 private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
-    val transitionSpecs = mutableListOf<TransitionSpec>()
+    val transitionSpecs = mutableListOf<TransitionSpecImpl>()
 
     override fun to(to: SceneKey, builder: TransitionBuilder.() -> Unit): TransitionSpec {
         return transition(from = null, to = to, builder)
@@ -63,14 +63,15 @@
         to: SceneKey?,
         builder: TransitionBuilder.() -> Unit,
     ): TransitionSpec {
-        val impl = TransitionBuilderImpl().apply(builder)
-        val spec =
-            TransitionSpec(
-                from,
-                to,
-                impl.transformations,
-                impl.spec,
+        fun transformationSpec(): TransformationSpecImpl {
+            val impl = TransitionBuilderImpl().apply(builder)
+            return TransformationSpecImpl(
+                progressSpec = impl.spec,
+                transformations = impl.transformations,
             )
+        }
+
+        val spec = TransitionSpecImpl(from, to, ::transformationSpec)
         transitionSpecs.add(spec)
         return spec
     }
@@ -143,7 +144,7 @@
 
         transformations.add(
             if (reversed) {
-                transformation.reverse()
+                transformation.reversed()
             } else {
                 transformation
             }
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 20693555..0cd11b9 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
@@ -42,7 +42,7 @@
      * Reverse this transformation. This is called when we use Transition(from = A, to = B) when
      * animating from B to A and there is no Transition(from = B, to = A) defined.
      */
-    fun reverse(): Transformation = this
+    fun reversed(): Transformation = this
 }
 
 internal class SharedElementTransformation(
@@ -77,10 +77,10 @@
     val delegate: PropertyTransformation<T>,
     override val range: TransformationRange,
 ) : PropertyTransformation<T> by delegate {
-    override fun reverse(): Transformation {
+    override fun reversed(): Transformation {
         return RangedPropertyTransformation(
-            delegate.reverse() as PropertyTransformation<T>,
-            range.reverse()
+            delegate.reversed() as PropertyTransformation<T>,
+            range.reversed()
         )
     }
 }
@@ -102,7 +102,7 @@
     }
 
     /** Reverse this range. */
-    fun reverse() = TransformationRange(start = reverseBound(end), end = reverseBound(start))
+    fun reversed() = TransformationRange(start = reverseBound(end), end = reverseBound(start))
 
     /** Get the progress of this range given the global [transitionProgress]. */
     fun progress(transitionProgress: Float): Float {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index e6224df..d9ce519 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -55,8 +55,8 @@
     ) {
         private var internalCurrentScene: SceneKey by mutableStateOf(SceneA)
 
-        private val layoutState: SceneTransitionLayoutState =
-            SceneTransitionLayoutState(internalCurrentScene)
+        private val layoutState =
+            SceneTransitionLayoutStateImpl(internalCurrentScene, EmptyTestTransitions)
 
         val mutableUserActionsA: MutableMap<UserAction, SceneKey> =
             mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
@@ -93,36 +93,24 @@
 
         private val layoutImpl =
             SceneTransitionLayoutImpl(
-                    onChangeScene = { internalCurrentScene = it },
-                    builder = scenesBuilder,
-                    transitions = EmptyTestTransitions,
                     state = layoutState,
+                    onChangeScene = { internalCurrentScene = it },
                     density = Density(1f),
                     edgeDetector = DefaultEdgeDetector,
                     transitionInterceptionThreshold = transitionInterceptionThreshold,
+                    builder = scenesBuilder,
                     coroutineScope = coroutineScope,
                 )
                 .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) }
 
-        val sceneGestureHandler =
-            SceneGestureHandler(
-                layoutImpl = layoutImpl,
-                orientation = Orientation.Vertical,
-                coroutineScope = coroutineScope,
-            )
-
-        val horizontalSceneGestureHandler =
-            SceneGestureHandler(
-                layoutImpl = layoutImpl,
-                orientation = Orientation.Horizontal,
-                coroutineScope = coroutineScope,
-            )
-
+        val sceneGestureHandler = layoutImpl.gestureHandler(Orientation.Vertical)
+        val horizontalSceneGestureHandler = layoutImpl.gestureHandler(Orientation.Horizontal)
         val draggable = sceneGestureHandler.draggable
 
         fun nestedScrollConnection(nestedScrollBehavior: NestedScrollBehavior) =
             SceneNestedScrollHandler(
-                    gestureHandler = sceneGestureHandler,
+                    layoutImpl,
+                    orientation = sceneGestureHandler.orientation,
                     topOrLeftBehavior = nestedScrollBehavior,
                     bottomOrRightBehavior = nestedScrollBehavior,
                 )
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index eeda8d46..c5b8d9a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -40,8 +40,8 @@
 
     @Test
     fun isTransitioningTo_transition() {
-        val state = SceneTransitionLayoutState(TestScenes.SceneA)
-        state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneB)
+        val state = SceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
+        state.startTransition(transition(from = TestScenes.SceneA, to = TestScenes.SceneB))
 
         assertThat(state.isTransitioning()).isTrue()
         assertThat(state.isTransitioning(from = TestScenes.SceneA)).isTrue()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index fa94b250..ef72992 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -55,7 +55,7 @@
 
         assertThat(transitions.transitionSpecs)
             .comparingElementsUsing(
-                Correspondence.transforming<TransitionSpec, Pair<SceneKey?, SceneKey?>>(
+                Correspondence.transforming<TransitionSpecImpl, Pair<SceneKey?, SceneKey?>>(
                     { it?.from to it?.to },
                     "has (from, to) equal to"
                 )
@@ -70,8 +70,8 @@
     @Test
     fun defaultTransitionSpec() {
         val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) }
-        val transition = transitions.transitionSpecs.single()
-        assertThat(transition.spec).isInstanceOf(SpringSpec::class.java)
+        val transformationSpec = transitions.transitionSpecs.single().transformationSpec()
+        assertThat(transformationSpec.progressSpec).isInstanceOf(SpringSpec::class.java)
     }
 
     @Test
@@ -79,9 +79,9 @@
         val transitions = transitions {
             from(TestScenes.SceneA, to = TestScenes.SceneB) { spec = tween(durationMillis = 42) }
         }
-        val transition = transitions.transitionSpecs.single()
-        assertThat(transition.spec).isInstanceOf(TweenSpec::class.java)
-        assertThat((transition.spec as TweenSpec).durationMillis).isEqualTo(42)
+        val transformationSpec = transitions.transitionSpecs.single().transformationSpec()
+        assertThat(transformationSpec.progressSpec).isInstanceOf(TweenSpec::class.java)
+        assertThat((transformationSpec.progressSpec as TweenSpec).durationMillis).isEqualTo(42)
     }
 
     @Test
@@ -90,9 +90,10 @@
             from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) }
         }
 
-        val transition = transitions.transitionSpecs.single()
-        assertThat(transition.transformations.size).isEqualTo(1)
-        assertThat(transition.transformations.single().range).isEqualTo(null)
+        val transformations =
+            transitions.transitionSpecs.single().transformationSpec().transformations
+        assertThat(transformations.size).isEqualTo(1)
+        assertThat(transformations.single().range).isEqualTo(null)
     }
 
     @Test
@@ -105,8 +106,9 @@
             }
         }
 
-        val transition = transitions.transitionSpecs.single()
-        assertThat(transition.transformations)
+        val transformations =
+            transitions.transitionSpecs.single().transformationSpec().transformations
+        assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
                 TransformationRange(start = 0.1f, end = 0.8f),
@@ -127,8 +129,9 @@
             }
         }
 
-        val transition = transitions.transitionSpecs.single()
-        assertThat(transition.transformations)
+        val transformations =
+            transitions.transitionSpecs.single().transformationSpec().transformations
+        assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
                 TransformationRange(start = 100 / 500f, end = 300 / 500f),
@@ -149,8 +152,9 @@
             }
         }
 
-        val transition = transitions.transitionSpecs.single()
-        assertThat(transition.transformations)
+        val transformations =
+            transitions.transitionSpecs.single().transformationSpec().transformations
+        assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
                 TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
@@ -170,9 +174,13 @@
 
         // Fetch the transition from B to A, which will automatically reverse the transition from A
         // to B we defined.
-        val transition =
-            transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA)
-        assertThat(transition.transformations)
+        val transformations =
+            transitions
+                .transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA)
+                .transformationSpec()
+                .transformations
+
+        assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
                 TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 695d888..f170135 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -18,7 +18,7 @@
 package com.android.keyguard
 
 import android.content.res.Configuration
-import android.hardware.biometrics.BiometricOverlayConstants
+import android.hardware.biometrics.BiometricRequestConstants
 import android.media.AudioManager
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper.RunWithLooper
@@ -59,6 +59,7 @@
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -235,6 +236,7 @@
                 sceneInteractor = sceneInteractor,
             )
 
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         underTest =
             KeyguardSecurityContainerController(
                 view,
@@ -763,16 +765,18 @@
 
     @Test
     fun sideFpsControllerShow() {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         underTest.updateSideFpsVisibility(/* isVisible= */ true)
         verify(sideFpsController)
             .show(
                 SideFpsUiRequestSource.PRIMARY_BOUNCER,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD
             )
     }
 
     @Test
     fun sideFpsControllerHide() {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         underTest.updateSideFpsVisibility(/* isVisible= */ false)
         verify(sideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
new file mode 100644
index 0000000..74f50d8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.accessibility.data.repository
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ColorCorrectionRepositoryImplTest : SysuiTestCase() {
+    companion object {
+        val TEST_USER_1 = UserHandle.of(1)!!
+        val TEST_USER_2 = UserHandle.of(2)!!
+    }
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val scope = TestScope(testDispatcher)
+    private val settings: FakeSettings = FakeSettings()
+
+    private lateinit var underTest: ColorCorrectionRepository
+
+    @Before
+    fun setUp() {
+        underTest =
+            ColorCorrectionRepositoryImpl(
+                testDispatcher,
+                settings,
+            )
+    }
+
+    @Test
+    fun isEnabled_initiallyGetsSettingsValue() =
+        scope.runTest {
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                1,
+                TEST_USER_1.identifier
+            )
+
+            underTest =
+                ColorCorrectionRepositoryImpl(
+                    testDispatcher,
+                    settings,
+                )
+
+            underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope)
+            runCurrent()
+
+            val actualValue: Boolean = underTest.isEnabled(TEST_USER_1).first()
+            Truth.assertThat(actualValue).isTrue()
+        }
+
+    @Test
+    fun isEnabled_settingUpdated_valueUpdated() =
+        scope.runTest {
+            underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope)
+
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                ColorCorrectionRepositoryImpl.DISABLED,
+                TEST_USER_1.identifier
+            )
+            runCurrent()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse()
+
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                ColorCorrectionRepositoryImpl.ENABLED,
+                TEST_USER_1.identifier
+            )
+            runCurrent()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue()
+
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                ColorCorrectionRepositoryImpl.DISABLED,
+                TEST_USER_1.identifier
+            )
+            runCurrent()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse()
+        }
+
+    @Test
+    fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() =
+        scope.runTest {
+            underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope)
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                ColorCorrectionRepositoryImpl.DISABLED,
+                TEST_USER_1.identifier
+            )
+            underTest.isEnabled(TEST_USER_2).launchIn(backgroundScope)
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                ColorCorrectionRepositoryImpl.DISABLED,
+                TEST_USER_2.identifier
+            )
+
+            runCurrent()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse()
+
+            settings.putIntForUser(
+                ColorCorrectionRepositoryImpl.SETTING_NAME,
+                ColorCorrectionRepositoryImpl.ENABLED,
+                TEST_USER_1.identifier
+            )
+            runCurrent()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue()
+            Truth.assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse()
+        }
+
+    @Test
+    fun setEnabled() =
+        scope.runTest {
+            val success = underTest.setIsEnabled(true, TEST_USER_1)
+            runCurrent()
+            Truth.assertThat(success).isTrue()
+
+            val actualValue =
+                settings.getIntForUser(
+                    ColorCorrectionRepositoryImpl.SETTING_NAME,
+                    TEST_USER_1.identifier
+                )
+            Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.ENABLED)
+        }
+
+    @Test
+    fun setDisabled() =
+        scope.runTest {
+            val success = underTest.setIsEnabled(false, TEST_USER_1)
+            runCurrent()
+            Truth.assertThat(success).isTrue()
+
+            val actualValue =
+                settings.getIntForUser(
+                    ColorCorrectionRepositoryImpl.SETTING_NAME,
+                    TEST_USER_1.identifier
+                )
+            Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.DISABLED)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index a1b801c..f8321b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -22,9 +22,9 @@
 import android.content.ComponentName
 import android.graphics.Insets
 import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN
 import android.hardware.biometrics.SensorLocationInternal
 import android.hardware.biometrics.SensorProperties
 import android.hardware.display.DisplayManager
@@ -65,6 +65,7 @@
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
@@ -144,6 +145,7 @@
 
     @Before
     fun setup() {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         displayRepository = FakeDisplayRepository()
         displayStateRepository = FakeDisplayStateRepository()
         keyguardBouncerRepository = FakeKeyguardBouncerRepository()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 90d36e7..a726b7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -17,12 +17,12 @@
 package com.android.systemui.biometrics
 
 import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.RequestReason
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
 import android.testing.TestableLooper.RunWithLooper
 import android.view.LayoutInflater
@@ -135,7 +135,7 @@
     }
 
     private fun withReason(
-        @ShowReason reason: Int,
+        @RequestReason reason: Int,
         isDebuggable: Boolean = false,
         enableDeviceEntryUdfpsRefactor: Boolean = false,
         block: () -> Unit,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 97ee526..dddcf18 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -43,7 +43,7 @@
 
 import android.graphics.Rect;
 import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.display.DisplayManager;
@@ -359,7 +359,7 @@
     @Test
     public void dozeTimeTick() throws RemoteException {
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
         mUdfpsController.dozeTimeTick();
         verify(mUdfpsView).dozeTimeTick();
@@ -455,7 +455,7 @@
     public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
         // GIVEN overlay was showing and the udfps bouncer is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
         // WHEN the overlay is hidden
@@ -469,7 +469,7 @@
     @Test
     public void showUdfpsOverlay_callsListener() throws RemoteException {
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         verify(mFingerprintManager).onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN,
@@ -479,7 +479,7 @@
     @Test
     public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
@@ -520,7 +520,7 @@
                             reset(mWindowManager);
                             mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID,
                                     mOpticalProps.sensorId,
-                                    BiometricOverlayConstants.REASON_ENROLL_ENROLLING,
+                                    BiometricRequestConstants.REASON_ENROLL_ENROLLING,
                                     mUdfpsOverlayControllerCallback);
                             mFgExecutor.runAllReady();
                             verify(mWindowManager).addView(any(), any());
@@ -555,7 +555,7 @@
 
         // Show the overlay.
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
         verify(mWindowManager).addView(any(), any());
 
@@ -637,7 +637,7 @@
 
         // Show the overlay.
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
         verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
 
@@ -720,7 +720,7 @@
 
         initUdfpsController(testParams.sensorProps);
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         // WHEN ACTION_DOWN is received
@@ -778,7 +778,7 @@
 
         // GIVEN that the overlay is showing and screen is on and fp is running
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
         // WHEN fingerprint is requested because of AOD interrupt
@@ -808,7 +808,7 @@
 
         // GIVEN AOD interrupt
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -886,7 +886,7 @@
 
         // GIVEN overlay is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
         if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
             when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
@@ -917,7 +917,7 @@
 
         // GIVEN AOD interrupt
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -958,7 +958,7 @@
         final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
                 givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
 
         if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
             // Configure UdfpsView to accept the ACTION_UP event
@@ -1019,7 +1019,7 @@
 
         // GIVEN screen off
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOff();
         mFgExecutor.runAllReady();
 
@@ -1041,7 +1041,7 @@
 
         // GIVEN showing overlay
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
 
@@ -1126,7 +1126,7 @@
         // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         if (a11y) {
@@ -1148,7 +1148,7 @@
         // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -1197,7 +1197,7 @@
                         -1 /* pointerId */, touchData);
 
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -1289,7 +1289,7 @@
     public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
         // GIVEN UDFPS overlay is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         // GIVEN there's been an AoD interrupt
@@ -1316,7 +1316,7 @@
             throws RemoteException {
         // GIVEN UDFPS overlay is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
 
         // GIVEN there's been an AoD interrupt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 90e0c19..a3bf3f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.utils.os.FakeHandler
@@ -105,8 +106,10 @@
         job.cancel()
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun shouldUpdateSideFps_show() = runTest {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         var count = 0
         val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this)
         repository.setPrimaryShow(true)
@@ -116,8 +119,10 @@
         job.cancel()
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun shouldUpdateSideFps_hide() = runTest {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         repository.setPrimaryShow(true)
         var count = 0
         val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this)
@@ -128,8 +133,10 @@
         job.cancel()
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun sideFpsShowing() = runTest {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         var sideFpsIsShowing = false
         val job = underTest.sideFpsShowing.onEach { sideFpsIsShowing = it }.launchIn(this)
         repository.setSideFpsShowing(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 45aca17..d6d5b23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -827,25 +827,6 @@
         }
 
     @Test
-    fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() =
-        testScope.runTest {
-            initCollectors()
-            allPreconditionsToRunFaceAuthAreTrue()
-
-            triggerFaceAuth(false)
-
-            authenticationCallback.value.onAuthenticationSucceeded(
-                mock(FaceManager.AuthenticationResult::class.java)
-            )
-
-            assertThat(authenticated()).isTrue()
-
-            keyguardRepository.setKeyguardGoingAway(true)
-
-            assertThat(authenticated()).isFalse()
-        }
-
-    @Test
     fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() =
         testScope.runTest {
             initCollectors()
@@ -906,6 +887,25 @@
         }
 
     @Test
+    fun isAuthenticatedIsResetToFalseWhenKeyguardDoneAnimationsFinished() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            keyguardRepository.keyguardDoneAnimationsFinished()
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
     fun detectDoesNotRunWhenFaceIsNotUsuallyAllowed() =
         testScope.runTest {
             testGatingCheckForDetect {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 2b7221e..6b7d263 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -304,4 +304,34 @@
             )
             assertThat(authenticationStatus).isNull()
         }
+
+    @Test
+    fun onBiometricRunningStateChanged_shouldUpdateIndicatorVisibility() =
+        testScope.runTest {
+            val shouldUpdateIndicatorVisibility by
+                collectLastValue(underTest.shouldUpdateIndicatorVisibility)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            assertThat(shouldUpdateIndicatorVisibility).isFalse()
+
+            invokeOnCallback {
+                it.onBiometricRunningStateChanged(false, BiometricSourceType.FINGERPRINT)
+            }
+            assertThat(shouldUpdateIndicatorVisibility).isTrue()
+        }
+
+    @Test
+    fun onStrongAuthStateChanged_shouldUpdateIndicatorVisibility() =
+        testScope.runTest {
+            val shouldUpdateIndicatorVisibility by
+                collectLastValue(underTest.shouldUpdateIndicatorVisibility)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            assertThat(shouldUpdateIndicatorVisibility).isFalse()
+
+            invokeOnCallback { it.onStrongAuthStateChanged(0) }
+            assertThat(shouldUpdateIndicatorVisibility).isTrue()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
new file mode 100644
index 0000000..8ee6d20
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -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.systemui.qs.tiles.impl.colorcorrection.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ColorCorrectionTileMapperTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig
+    private val subtitleArray =
+        context.resources.getStringArray(R.array.tile_states_color_correction)
+    // Using lazy (versus =) to make sure we override the right context -- see b/311612168
+    private val mapper by lazy {
+        ColorCorrectionTileMapper(
+            context.orCreateTestableResources
+                .apply { addOverride(R.drawable.ic_qs_color_correction, TestStubDrawable()) }
+                .resources,
+            context.theme
+        )
+    }
+
+    @Test
+    fun disabledModel() {
+        val inputModel = ColorCorrectionTileModel(false)
+
+        val outputState = mapper.map(colorCorrectionTileConfig, inputModel)
+
+        val expectedState =
+            createColorCorrectionTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1])
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun enabledModel() {
+        val inputModel = ColorCorrectionTileModel(true)
+
+        val outputState = mapper.map(colorCorrectionTileConfig, inputModel)
+
+        val expectedState =
+            createColorCorrectionTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2])
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    private fun createColorCorrectionTileState(
+        activationState: QSTileState.ActivationState,
+        secondaryLabel: String
+    ): QSTileState {
+        val label = context.getString(R.string.quick_settings_color_correction_label)
+        return QSTileState(
+            { Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null) },
+            label,
+            activationState,
+            secondaryLabel,
+            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+            label,
+            null,
+            QSTileState.SideViewIcon.None,
+            QSTileState.EnabledState.ENABLED,
+            Switch::class.qualifiedName
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
new file mode 100644
index 0000000..8c612ac
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.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.qs.tiles.impl.colorcorrection.domain.interactor
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toCollection
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ColorCorrectionTileDataInteractorTest : SysuiTestCase() {
+
+    private val colorCorrectionRepository = FakeColorCorrectionRepository()
+    private val underTest: ColorCorrectionTileDataInteractor =
+        ColorCorrectionTileDataInteractor(colorCorrectionRepository)
+
+    @Test
+    fun alwaysAvailable() = runTest {
+        val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
+
+        assertThat(availability).hasSize(1)
+        assertThat(availability.last()).isTrue()
+    }
+
+    @Test
+    fun dataMatchesTheRepository() = runTest {
+        val dataList: List<ColorCorrectionTileModel> by
+            collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))
+        runCurrent()
+
+        colorCorrectionRepository.setIsEnabled(true, TEST_USER)
+        runCurrent()
+
+        colorCorrectionRepository.setIsEnabled(false, TEST_USER)
+        runCurrent()
+
+        assertThat(dataList).hasSize(3)
+        assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false))
+    }
+
+    private companion object {
+        val TEST_USER = UserHandle.of(1)!!
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..3049cc0
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.qs.tiles.impl.colorcorrection.domain.interactor
+
+import android.os.UserHandle
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() {
+
+    private val testUser = UserHandle.CURRENT
+    private val repository = FakeColorCorrectionRepository()
+    private val inputHandler = FakeQSTileIntentUserInputHandler()
+
+    private val underTest =
+        ColorCorrectionUserActionInteractor(
+            repository,
+            inputHandler,
+        )
+
+    @Test
+    fun handleClickWhenEnabled() = runTest {
+        val wasEnabled = true
+        repository.setIsEnabled(wasEnabled, testUser)
+
+        underTest.handleInput(QSTileInputTestKtx.click(ColorCorrectionTileModel(wasEnabled)))
+
+        assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled)
+    }
+
+    @Test
+    fun handleClickWhenDisabled() = runTest {
+        val wasEnabled = false
+        repository.setIsEnabled(wasEnabled, testUser)
+
+        underTest.handleInput(QSTileInputTestKtx.click(ColorCorrectionTileModel(wasEnabled)))
+
+        assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled)
+    }
+
+    @Test
+    fun handleLongClickWhenDisabled() = runTest {
+        val enabled = false
+
+        underTest.handleInput(QSTileInputTestKtx.longClick(ColorCorrectionTileModel(enabled)))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+            assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
+        }
+    }
+
+    @Test
+    fun handleLongClickWhenEnabled() = runTest {
+        val enabled = true
+
+        underTest.handleInput(QSTileInputTestKtx.longClick(ColorCorrectionTileModel(enabled)))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+            assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
+        }
+    }
+}
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index 4d95220..fc4bf8a 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.SideFpsLottieViewWrapper
+<com.android.systemui.biometrics.SideFpsIndicatorView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/sidefps_animation"
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
index 80f70a0..3064829 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
@@ -37,6 +37,10 @@
     val hasSfps: Boolean
         get() = hasFingerprint && fingerprintProperties!!.isAnySidefpsType
 
+    /** If UDFPS authentication is available. */
+    val hasUdfps: Boolean
+        get() = hasFingerprint && fingerprintProperties!!.isAnyUdfpsType
+
     /** If fingerprint authentication is available (and [faceProperties] is non-null). */
     val hasFace: Boolean
         get() = faceProperties != null
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index df7182b..0169f59 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -49,6 +49,7 @@
     public static final int CUJ_APP_SWIPE_TO_RECENTS = Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
     public static final int CUJ_OPEN_SEARCH_RESULT = Cuj.CUJ_LAUNCHER_OPEN_SEARCH_RESULT;
     public static final int CUJ_LAUNCHER_UNFOLD_ANIM = Cuj.CUJ_LAUNCHER_UNFOLD_ANIM;
+    public static final int CUJ_SEARCH_QSB_OPEN = Cuj.CUJ_LAUNCHER_SEARCH_QSB_OPEN;
 
     @IntDef({
             CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -66,6 +67,7 @@
             CUJ_CLOSE_ALL_APPS_TO_HOME,
             CUJ_OPEN_SEARCH_RESULT,
             CUJ_LAUNCHER_UNFOLD_ANIM,
+            CUJ_SEARCH_QSB_OPEN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0a4378e..cce2018 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -38,7 +38,7 @@
 import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.media.AudioManager;
 import android.metrics.LogMaker;
 import android.os.SystemClock;
@@ -74,6 +74,7 @@
 import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate;
 import com.android.systemui.biometrics.SideFpsController;
 import com.android.systemui.biometrics.SideFpsUiRequestSource;
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor;
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingA11yDelegate;
@@ -486,7 +487,11 @@
         mSceneContainerFlags = sceneContainerFlags;
         mGlobalSettings = globalSettings;
         mSessionTracker = sessionTracker;
-        mSideFpsController = sideFpsController;
+        if (SideFpsControllerRefactor.isEnabled()) {
+            mSideFpsController = Optional.empty();
+        } else {
+            mSideFpsController = sideFpsController;
+        }
         mFalsingA11yDelegate = falsingA11yDelegate;
         mTelephonyManager = telephonyManager;
         mViewMediatorCallback = viewMediatorCallback;
@@ -569,12 +574,14 @@
         mView.clearFocus();
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /**
      * Shows and hides the side finger print sensor animation.
      *
      * @param isVisible sets whether we show or hide the side fps animation
      */
     public void updateSideFpsVisibility(boolean isVisible) {
+        SideFpsControllerRefactor.assertInLegacyMode();
         if (!mSideFpsController.isPresent()) {
             return;
         }
@@ -582,7 +589,7 @@
         if (isVisible) {
             mSideFpsController.get().show(
                     SideFpsUiRequestSource.PRIMARY_BOUNCER,
-                    BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+                    BiometricRequestConstants.REASON_AUTH_KEYGUARD
             );
         } else {
             mSideFpsController.get().hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
index ca859de..24aa11e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,61 +16,16 @@
 
 package com.android.systemui.accessibility
 
-import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.ColorCorrectionTile
-import com.android.systemui.qs.tiles.ColorInversionTile
-import com.android.systemui.qs.tiles.DreamTile
-import com.android.systemui.qs.tiles.FontScalingTile
-import com.android.systemui.qs.tiles.NightDisplayTile
-import com.android.systemui.qs.tiles.OneHandedModeTile
-import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
+import com.android.systemui.accessibility.data.repository.ColorCorrectionRepositoryImpl
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
 import dagger.Binds
 import dagger.Module
-import dagger.multibindings.IntoMap
-import dagger.multibindings.StringKey
 
-@Module
+@Module(includes = [QSAccessibilityModule::class])
 interface AccessibilityModule {
-
-    /** Inject ColorInversionTile into tileMap in QSModule */
     @Binds
-    @IntoMap
-    @StringKey(ColorInversionTile.TILE_SPEC)
-    fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*>
-
-    /** Inject NightDisplayTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(NightDisplayTile.TILE_SPEC)
-    fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*>
-
-    /** Inject ReduceBrightColorsTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(ReduceBrightColorsTile.TILE_SPEC)
-    fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*>
-
-    /** Inject OneHandedModeTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(OneHandedModeTile.TILE_SPEC)
-    fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*>
-
-    /** Inject ColorCorrectionTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(ColorCorrectionTile.TILE_SPEC)
-    fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*>
-
-    /** Inject DreamTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(DreamTile.TILE_SPEC)
-    fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*>
-
-    /** Inject FontScalingTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(FontScalingTile.TILE_SPEC)
-    fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*>
+    abstract fun colorCorrectionRepository(
+        impl: ColorCorrectionRepositoryImpl
+    ): ColorCorrectionRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt
new file mode 100644
index 0000000..6483ae4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.data.repository
+
+import android.os.UserHandle
+import android.provider.Settings.Secure
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+
+/** Provides data related to color correction. */
+interface ColorCorrectionRepository {
+    /** Observable for whether color correction is enabled */
+    fun isEnabled(userHandle: UserHandle): Flow<Boolean>
+
+    /** Sets color correction enabled state. */
+    suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean
+}
+
+@SysUISingleton
+class ColorCorrectionRepositoryImpl
+@Inject
+constructor(
+    @Background private val bgCoroutineContext: CoroutineContext,
+    private val secureSettings: SecureSettings,
+) : ColorCorrectionRepository {
+
+    companion object {
+        const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED
+        const val DISABLED = 0
+        const val ENABLED = 1
+    }
+
+    override fun isEnabled(userHandle: UserHandle): Flow<Boolean> =
+        secureSettings
+            .observerFlow(userHandle.identifier, SETTING_NAME)
+            .onStart { emit(Unit) }
+            .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED }
+            .distinctUntilChanged()
+            .flowOn(bgCoroutineContext)
+
+    override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean =
+        withContext(bgCoroutineContext) {
+            secureSettings.putIntForUser(
+                SETTING_NAME,
+                if (isEnabled) ENABLED else DISABLED,
+                userHandle.identifier
+            )
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
new file mode 100644
index 0000000..df7fdb8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.accessibility.qs
+
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.ColorCorrectionTile
+import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.DreamTile
+import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.qs.tiles.OneHandedModeTile
+import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrectionTileMapper
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.res.R
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface QSAccessibilityModule {
+
+    /** Inject ColorInversionTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ColorInversionTile.TILE_SPEC)
+    fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*>
+
+    /** Inject NightDisplayTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(NightDisplayTile.TILE_SPEC)
+    fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*>
+
+    /** Inject ReduceBrightColorsTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ReduceBrightColorsTile.TILE_SPEC)
+    fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*>
+
+    /** Inject OneHandedModeTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(OneHandedModeTile.TILE_SPEC)
+    fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*>
+
+    /** Inject ColorCorrectionTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ColorCorrectionTile.TILE_SPEC)
+    fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*>
+
+    /** Inject DreamTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(DreamTile.TILE_SPEC)
+    fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*>
+
+    /** Inject FontScalingTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(FontScalingTile.TILE_SPEC)
+    fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*>
+
+    companion object {
+
+        const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
+
+        @Provides
+        @IntoMap
+        @StringKey(COLOR_CORRECTION_TILE_SPEC)
+        fun provideColorCorrectionTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+            QSTileConfig(
+                tileSpec = TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
+                uiConfig =
+                    QSTileUIConfig.Resource(
+                        iconRes = R.drawable.ic_qs_color_correction,
+                        labelRes = R.string.quick_settings_color_correction_label,
+                    ),
+                instanceId = uiEventLogger.getNewInstanceId(),
+            )
+
+        /** Inject ColorCorrectionTile into tileViewModelMap in QSModule */
+        @Provides
+        @IntoMap
+        @StringKey(COLOR_CORRECTION_TILE_SPEC)
+        fun provideColorCorrectionTileViewModel(
+            factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>,
+            mapper: ColorCorrectionTileMapper,
+            stateInteractor: ColorCorrectionTileDataInteractor,
+            userActionInteractor: ColorCorrectionUserActionInteractor
+        ): QSTileViewModel =
+            factory.create(
+                TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
+                userActionInteractor,
+                stateInteractor,
+                mapper,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 877afce7..5fba761 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -70,6 +70,7 @@
 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
@@ -88,6 +89,8 @@
 
 import dagger.Lazy;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -101,7 +104,6 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import kotlin.Unit;
 import kotlinx.coroutines.CoroutineScope;
 
 /**
@@ -317,7 +319,9 @@
 
         mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
         if (mSidefpsProps != null) {
-            mSideFpsController = mSidefpsControllerFactory.get();
+            if (!SideFpsControllerRefactor.isEnabled()) {
+                mSideFpsController = mSidefpsControllerFactory.get();
+            }
         }
 
         mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() {
@@ -1194,7 +1198,7 @@
      * Whether the passed userId has enrolled SFPS.
      */
     public boolean isSfpsEnrolled(int userId) {
-        if (mSideFpsController == null) {
+        if (mSidefpsProps == null) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 91cee9e..ac99fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -23,9 +23,9 @@
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffColorFilter
 import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
 import android.hardware.biometrics.SensorLocationInternal
 import android.hardware.display.DisplayManager
 import android.hardware.fingerprint.FingerprintManager
@@ -58,6 +58,7 @@
 import com.android.keyguard.KeyguardPINView
 import com.android.systemui.Dumpable
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -91,7 +92,7 @@
     @Main private val mainExecutor: DelayableExecutor,
     @Main private val handler: Handler,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
-    @Application private val scope: CoroutineScope,
+    @Application private val applicationScope: CoroutineScope,
     dumpManager: DumpManager,
     fpsUnlockTracker: FpsUnlockTracker
 ) : Dumpable {
@@ -110,7 +111,7 @@
             handler,
             sensorProps,
             { reason -> onOrientationChanged(reason) },
-            BiometricOverlayConstants.REASON_UNKNOWN
+            BiometricRequestConstants.REASON_UNKNOWN
         )
 
     @VisibleForTesting val orientationListener = orientationReasonListener.orientationListener
@@ -169,25 +170,27 @@
             }
 
     init {
-        fpsUnlockTracker.startTracking()
-        fingerprintManager?.setSidefpsController(
-            object : ISidefpsController.Stub() {
-                override fun show(
-                    sensorId: Int,
-                    @BiometricOverlayConstants.ShowReason reason: Int
-                ) =
-                    if (reason.isReasonToAutoShow(activityTaskManager)) {
-                        show(SideFpsUiRequestSource.AUTO_SHOW, reason)
-                    } else {
-                        hide(SideFpsUiRequestSource.AUTO_SHOW)
-                    }
+        if (!SideFpsControllerRefactor.isEnabled) {
+            fpsUnlockTracker.startTracking()
+            fingerprintManager?.setSidefpsController(
+                object : ISidefpsController.Stub() {
+                    override fun show(
+                        sensorId: Int,
+                        @BiometricRequestConstants.RequestReason reason: Int
+                    ) =
+                        if (reason.isReasonToAutoShow(activityTaskManager)) {
+                            show(SideFpsUiRequestSource.AUTO_SHOW, reason)
+                        } else {
+                            hide(SideFpsUiRequestSource.AUTO_SHOW)
+                        }
 
-                override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW)
-            }
-        )
-        listenForAlternateBouncerVisibility()
+                    override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW)
+                }
+            )
+            listenForAlternateBouncerVisibility()
 
-        dumpManager.registerDumpable(this)
+            dumpManager.registerDumpable(this)
+        }
     }
 
     private fun listenForAlternateBouncerVisibility() {
@@ -195,7 +198,7 @@
             alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, "SideFpsController")
         }
 
-        scope.launch {
+        applicationScope.launch {
             alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
                 if (isVisible) {
                     show(SideFpsUiRequestSource.ALTERNATE_BOUNCER, REASON_AUTH_KEYGUARD)
@@ -209,8 +212,10 @@
     /** Shows the side fps overlay if not already shown. */
     fun show(
         request: SideFpsUiRequestSource,
-        @BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN
+        @BiometricRequestConstants.RequestReason
+        reason: Int = BiometricRequestConstants.REASON_UNKNOWN
     ) {
+        SideFpsControllerRefactor.assertInLegacyMode()
         if (!displayStateInteractor.isInRearDisplayMode.value) {
             requests.add(request)
             mainExecutor.execute {
@@ -229,6 +234,7 @@
 
     /** Hides the fps overlay if shown. */
     fun hide(request: SideFpsUiRequestSource) {
+        SideFpsControllerRefactor.assertInLegacyMode()
         requests.remove(request)
         mainExecutor.execute {
             if (requests.isEmpty()) {
@@ -239,6 +245,7 @@
 
     /** Hide the arrow indicator. */
     fun hideIndicator() {
+        SideFpsControllerRefactor.assertInLegacyMode()
         val lottieAnimationView =
             overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView?
         lottieAnimationView?.visibility = INVISIBLE
@@ -246,6 +253,7 @@
 
     /** Show the arrow indicator. */
     fun showIndicator() {
+        SideFpsControllerRefactor.assertInLegacyMode()
         val lottieAnimationView =
             overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView?
         lottieAnimationView?.visibility = VISIBLE
@@ -279,13 +287,13 @@
         pw.println("currentRotation=${displayInfo.rotation}")
     }
 
-    private fun onOrientationChanged(@BiometricOverlayConstants.ShowReason reason: Int) {
+    private fun onOrientationChanged(@BiometricRequestConstants.RequestReason reason: Int) {
         if (overlayView != null) {
             createOverlayForDisplay(reason)
         }
     }
 
-    private fun createOverlayForDisplay(@BiometricOverlayConstants.ShowReason reason: Int) {
+    private fun createOverlayForDisplay(@BiometricRequestConstants.RequestReason reason: Int) {
         val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
         overlayView = view
         val display = context.display!!
@@ -395,7 +403,7 @@
 /** Returns [True] when the device has a side fingerprint sensor. */
 fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null
 
-@BiometricOverlayConstants.ShowReason
+@BiometricRequestConstants.RequestReason
 private fun Int.isReasonToAutoShow(activityTaskManager: ActivityTaskManager): Boolean =
     when (this) {
         REASON_AUTH_KEYGUARD -> false
@@ -434,7 +442,7 @@
 
 private fun LottieAnimationView.addOverlayDynamicColor(
     context: Context,
-    @BiometricOverlayConstants.ShowReason reason: Int
+    @BiometricRequestConstants.RequestReason reason: Int
 ) {
     fun update() {
         val isKeyguard = reason == REASON_AUTH_KEYGUARD
@@ -501,7 +509,7 @@
     handler: Handler,
     sensorProps: FingerprintSensorPropertiesInternal,
     onOrientationChanged: (reason: Int) -> Unit,
-    @BiometricOverlayConstants.ShowReason var reason: Int
+    @BiometricRequestConstants.RequestReason var reason: Int
 ) {
     val orientationListener =
         BiometricDisplayListener(
@@ -516,7 +524,7 @@
 
 /**
  * The source of a request to show the side fps visual indicator. This is distinct from
- * [BiometricOverlayConstants] which corrresponds with the reason fingerprint authentication is
+ * [BiometricRequestConstants] which corresponds with the reason fingerprint authentication is
  * requested.
  */
 enum class SideFpsUiRequestSource {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
rename to packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt
index e98f6db..d5e25ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt
@@ -19,6 +19,6 @@
 import android.util.AttributeSet
 import com.android.systemui.util.wrapper.LottieViewWrapper
 
-class SideFpsLottieViewWrapper
+class SideFpsIndicatorView
 @JvmOverloads
 constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index bb6ef41..65668b56 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -19,10 +19,10 @@
 import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT;
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
-import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING;
+import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR;
 
 import static com.android.internal.util.LatencyTracker.ACTION_UDFPS_ILLUMINATE;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -106,6 +106,8 @@
 
 import dagger.Lazy;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -115,8 +117,6 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import kotlin.Unit;
-
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 452cd6a..dae6d08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -21,13 +21,13 @@
 import android.content.Context
 import android.graphics.PixelFormat
 import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import android.hardware.biometrics.BiometricRequestConstants.RequestReason
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
 import android.os.Build
 import android.os.RemoteException
@@ -96,7 +96,7 @@
     private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
     private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
     val requestId: Long,
-    @ShowReason val requestReason: Int,
+    @RequestReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
     private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
     private val activityLaunchAnimator: ActivityLaunchAnimator,
@@ -461,7 +461,7 @@
     }
 }
 
-@ShowReason
+@RequestReason
 private fun Int.isImportantForAccessibility() =
     this == REASON_ENROLL_FIND_SENSOR ||
             this == REASON_ENROLL_ENROLLING ||
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
index e3dbcb5..88b9e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
@@ -18,13 +18,13 @@
 
 import android.content.Context
 import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
 import android.util.Log
 import android.view.LayoutInflater
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 72fcfe7..8ae6f87 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -18,8 +18,11 @@
 
 import android.content.res.Resources
 import com.android.internal.R
+import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.EllipseOverlapDetectorParams
 import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
+import com.android.systemui.biometrics.data.repository.BiometricStatusRepositoryImpl
 import com.android.systemui.biometrics.data.repository.DisplayStateRepository
 import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
@@ -33,11 +36,14 @@
 import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
 import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
 import com.android.systemui.biometrics.udfps.OverlapDetector
+import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.util.concurrency.ThreadFactory
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
 import java.util.concurrent.Executor
 import javax.inject.Qualifier
 
@@ -46,6 +52,11 @@
 interface BiometricsModule {
 
     @Binds
+    @IntoMap
+    @ClassKey(SideFpsOverlayViewBinder::class)
+    fun bindsSideFpsOverlayViewBinder(viewBinder: SideFpsOverlayViewBinder): CoreStartable
+
+    @Binds
     @SysUISingleton
     fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository
 
@@ -57,6 +68,10 @@
 
     @Binds
     @SysUISingleton
+    fun biometricStatusRepository(impl: BiometricStatusRepositoryImpl): BiometricStatusRepository
+
+    @Binds
+    @SysUISingleton
     fun fingerprintRepository(
         impl: FingerprintPropertyRepositoryImpl
     ): FingerprintPropertyRepository
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
new file mode 100644
index 0000000..ad2136a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.AuthenticationStateListener
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.shareIn
+
+/** A repository for the state of biometric authentication. */
+interface BiometricStatusRepository {
+    /**
+     * The logical reason for the current fingerprint auth operation if one is on-going, otherwise
+     * [NotRunning].
+     */
+    val fingerprintAuthenticationReason: Flow<AuthenticationReason>
+}
+
+@SysUISingleton
+class BiometricStatusRepositoryImpl
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val biometricManager: BiometricManager?
+) : BiometricStatusRepository {
+
+    override val fingerprintAuthenticationReason: Flow<AuthenticationReason> =
+        conflatedCallbackFlow {
+                val updateFingerprintAuthenticateReason = { reason: AuthenticationReason ->
+                    trySendWithFailureLogging(
+                        reason,
+                        TAG,
+                        "Error sending fingerprintAuthenticateReason reason"
+                    )
+                }
+
+                val authenticationStateListener =
+                    object : AuthenticationStateListener.Stub() {
+                        override fun onAuthenticationStarted(requestReason: Int) {
+                            val authenticationReason =
+                                when (requestReason) {
+                                    REASON_AUTH_BP ->
+                                        AuthenticationReason.BiometricPromptAuthentication
+                                    REASON_AUTH_KEYGUARD ->
+                                        AuthenticationReason.DeviceEntryAuthentication
+                                    REASON_AUTH_OTHER -> AuthenticationReason.OtherAuthentication
+                                    REASON_AUTH_SETTINGS ->
+                                        AuthenticationReason.SettingsAuthentication(
+                                            SettingsOperations.OTHER
+                                        )
+                                    REASON_ENROLL_ENROLLING ->
+                                        AuthenticationReason.SettingsAuthentication(
+                                            SettingsOperations.ENROLL_ENROLLING
+                                        )
+                                    REASON_ENROLL_FIND_SENSOR ->
+                                        AuthenticationReason.SettingsAuthentication(
+                                            SettingsOperations.ENROLL_FIND_SENSOR
+                                        )
+                                    else -> AuthenticationReason.Unknown
+                                }
+                            updateFingerprintAuthenticateReason(authenticationReason)
+                        }
+
+                        override fun onAuthenticationStopped() {
+                            updateFingerprintAuthenticateReason(AuthenticationReason.NotRunning)
+                        }
+                    }
+
+                updateFingerprintAuthenticateReason(AuthenticationReason.NotRunning)
+                biometricManager?.registerAuthenticationStateListener(authenticationStateListener)
+                awaitClose {
+                    biometricManager?.unregisterAuthenticationStateListener(
+                        authenticationStateListener
+                    )
+                }
+            }
+            .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
+
+    companion object {
+        private const val TAG = "BiometricStatusRepositoryImpl"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
index b9b2fd8..ec3fd9f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.biometrics.domain
 
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
 import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -38,6 +40,12 @@
 
     @Binds
     @SysUISingleton
+    fun providesBiometricStatusInteractor(
+        impl: BiometricStatusInteractorImpl
+    ): BiometricStatusInteractor
+
+    @Binds
+    @SysUISingleton
     fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
new file mode 100644
index 0000000..55a2d3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain.interactor
+
+import android.app.ActivityTaskManager
+import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for interacting with biometric authentication state. */
+interface BiometricStatusInteractor {
+    /**
+     * The logical reason for the current side fingerprint sensor auth operation if one is on-going,
+     * filtered for when the overlay should be shown, otherwise [NotRunning].
+     */
+    val sfpsAuthenticationReason: Flow<AuthenticationReason>
+}
+
+class BiometricStatusInteractorImpl
+@Inject
+constructor(
+    private val activityTaskManager: ActivityTaskManager,
+    biometricStatusRepository: BiometricStatusRepository,
+) : BiometricStatusInteractor {
+
+    override val sfpsAuthenticationReason: Flow<AuthenticationReason> =
+        biometricStatusRepository.fingerprintAuthenticationReason.map { reason: AuthenticationReason
+            ->
+            if (reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)) {
+                reason
+            } else {
+                AuthenticationReason.NotRunning
+            }
+        }
+
+    companion object {
+        private const val TAG = "BiometricStatusInteractor"
+    }
+}
+
+/** True if the sfps overlay should always be updated for this request source, false otherwise. */
+private fun AuthenticationReason.isReasonToAlwaysUpdateSfpsOverlay(
+    activityTaskManager: ActivityTaskManager
+): Boolean =
+    when (this) {
+        AuthenticationReason.DeviceEntryAuthentication -> false
+        AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER) ->
+            when (activityTaskManager.topClass()) {
+                // TODO(b/186176653): exclude fingerprint overlays from this list view
+                "com.android.settings.biometrics.fingerprint.FingerprintSettings" -> false
+                else -> true
+            }
+        else -> true
+    }
+
+internal fun ActivityTaskManager.topClass(): String =
+    getTasks(1).firstOrNull()?.topActivity?.className ?: ""
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index f513799..f4231ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -141,7 +141,6 @@
                             }
                         }
                     }
-
                 SideFpsSensorLocation(
                     left = sensorLeft,
                     top = sensorTop,
@@ -149,7 +148,15 @@
                     isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation
                 )
             }
-            .distinctUntilChanged()
+            .distinctUntilChanged(
+                areEquivalent = { old: SideFpsSensorLocation, new: SideFpsSensorLocation ->
+                    old.left == new.left &&
+                        old.top == new.top &&
+                        old.length == new.length &&
+                        old.isSensorVerticalInDefaultOrientation ==
+                            new.isSensorVerticalInDefaultOrientation
+                }
+            )
             .onEach {
                 logger.sensorLocationStateChanged(
                     it.left,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt
new file mode 100644
index 0000000..899b07e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.shared
+
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+import com.android.systemui.shared.Flags
+
+/** Helper for reading or using the sidefps controller refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object SideFpsControllerRefactor {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.sidefpsControllerRefactor()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt
new file mode 100644
index 0000000..0c3debbe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.shared.model
+
+/**
+ * The logical reason for a fingerprint auth operation if one is on-going, otherwise [NotRunning].
+ */
+sealed interface AuthenticationReason {
+    /** Device entry requested authentication */
+    data object DeviceEntryAuthentication : AuthenticationReason
+
+    /** Settings requested authentication */
+    data class SettingsAuthentication(val settingsOperation: SettingsOperations) :
+        AuthenticationReason
+
+    /** App requested authentication */
+    data object BiometricPromptAuthentication : AuthenticationReason
+
+    /** Authentication requested for other reason */
+    data object OtherAuthentication : AuthenticationReason
+
+    /** Authentication requested for unknown reason */
+    data object Unknown : AuthenticationReason
+
+    /** Authentication is not running */
+    data object NotRunning : AuthenticationReason
+
+    /** Settings operations that request biometric authentication */
+    enum class SettingsOperations {
+        ENROLL_ENROLLING,
+        ENROLL_FIND_SENSOR,
+        OTHER
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
copy to packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt
index e98f6db..0b30055 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt
@@ -13,12 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.biometrics
 
-import android.content.Context
-import android.util.AttributeSet
-import com.android.systemui.util.wrapper.LottieViewWrapper
+package com.android.systemui.biometrics.shared.model
 
-class SideFpsLottieViewWrapper
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs)
+import com.airbnb.lottie.model.KeyPath
+
+/** Represents properties of a LottieAnimationView callback */
+data class LottieCallback(val keypath: KeyPath, val color: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 32d9067b..90e4a38 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -376,6 +376,22 @@
                     }
                 }
 
+                // Talkback directional guidance
+                backgroundView.setOnHoverListener { _, event ->
+                    launch {
+                        viewModel.onAnnounceAccessibilityHint(
+                            event,
+                            accessibilityManager.isTouchExplorationEnabled
+                        )
+                    }
+                    false
+                }
+                launch {
+                    viewModel.accessibilityHint.collect { message ->
+                        if (message.isNotBlank()) view.announceForAccessibility(message)
+                    }
+                }
+
                 // Play haptics
                 launch {
                     viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
new file mode 100644
index 0000000..a8c9446
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import android.content.Context
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieProperty
+import com.android.app.animation.Interpolators
+import com.android.keyguard.KeyguardPINView
+import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.FpsUnlockTracker
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+/** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */
+@SysUISingleton
+class SideFpsOverlayViewBinder
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    @Application private val applicationContext: Context,
+    private val biometricStatusInteractor: BiometricStatusInteractor,
+    private val displayStateInteractor: DisplayStateInteractor,
+    private val deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
+    private val fpsUnlockTracker: FpsUnlockTracker,
+    private val layoutInflater: LayoutInflater,
+    private val sideFpsProgressBarViewModel: SideFpsProgressBarViewModel,
+    private val sfpsSensorInteractor: SideFpsSensorInteractor,
+    private val windowManager: WindowManager
+) : CoreStartable {
+
+    override fun start() {
+        if (!SideFpsControllerRefactor.isEnabled) {
+            return
+        }
+        applicationScope
+            .launch {
+                combine(
+                        biometricStatusInteractor.sfpsAuthenticationReason,
+                        deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
+                        sideFpsProgressBarViewModel.isVisible,
+                        ::Triple
+                    )
+                    .sample(displayStateInteractor.isInRearDisplayMode, ::Pair)
+                    .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
+                        val (
+                            systemServerAuthReason,
+                            showIndicatorForDeviceEntry,
+                            progressBarIsVisible) =
+                            combinedFlows
+                        if (!isInRearDisplayMode) {
+                            if (progressBarIsVisible) {
+                                hide()
+                            } else if (systemServerAuthReason != NotRunning) {
+                                show()
+                            } else if (showIndicatorForDeviceEntry) {
+                                show()
+                            } else {
+                                hide()
+                            }
+                        }
+                    }
+            }
+            .invokeOnCompletion { fpsUnlockTracker.stopTracking() }
+    }
+
+    private var overlayView: View? = null
+    private var lottie: LottieAnimationView? = null
+
+    /** Show the side fingerprint sensor indicator */
+    private fun show() {
+        overlayView?.let {
+            if (it.isAttachedToWindow) {
+                lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
+                lottie?.pauseAnimation()
+                windowManager.removeView(it)
+            }
+        }
+
+        overlayView = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+        val overlayViewModel =
+            SideFpsOverlayViewModel(
+                applicationContext,
+                biometricStatusInteractor,
+                deviceEntrySideFpsOverlayInteractor,
+                displayStateInteractor,
+                sfpsSensorInteractor,
+                sideFpsProgressBarViewModel
+            )
+        bind(overlayView!!, overlayViewModel, fpsUnlockTracker, windowManager)
+        overlayView!!.visibility = View.INVISIBLE
+        windowManager.addView(overlayView, overlayViewModel.defaultOverlayViewParams)
+    }
+
+    /** Hide the side fingerprint sensor indicator */
+    private fun hide() {
+        if (overlayView != null) {
+            windowManager.removeView(overlayView)
+            overlayView = null
+        }
+    }
+
+    companion object {
+        private const val TAG = "SideFpsOverlayViewBinder"
+
+        /** Binds overlayView (side fingerprint sensor indicator view) to SideFpsOverlayViewModel */
+        fun bind(
+            overlayView: View,
+            viewModel: SideFpsOverlayViewModel,
+            fpsUnlockTracker: FpsUnlockTracker,
+            windowManager: WindowManager
+        ) {
+            overlayView.repeatWhenAttached {
+                fpsUnlockTracker.startTracking()
+
+                val lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
+                lottie.addLottieOnCompositionLoadedListener { composition: LottieComposition ->
+                    viewModel.setLottieBounds(composition.bounds)
+                    overlayView.visibility = View.VISIBLE
+                }
+                it.alpha = 0f
+                val overlayShowAnimator =
+                    it.animate()
+                        .alpha(1f)
+                        .setDuration(KeyguardPINView.ANIMATION_DURATION)
+                        .setInterpolator(Interpolators.ALPHA_IN)
+
+                overlayShowAnimator.start()
+
+                it.setAccessibilityDelegate(
+                    object : View.AccessibilityDelegate() {
+                        override fun dispatchPopulateAccessibilityEvent(
+                            host: View,
+                            event: AccessibilityEvent
+                        ): Boolean {
+                            return if (
+                                event.getEventType() ===
+                                    android.view.accessibility.AccessibilityEvent
+                                        .TYPE_WINDOW_STATE_CHANGED
+                            ) {
+                                true
+                            } else {
+                                super.dispatchPopulateAccessibilityEvent(host, event)
+                            }
+                        }
+                    }
+                )
+
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    launch {
+                        viewModel.lottieCallbacks.collect { callbacks ->
+                            lottie.addOverlayDynamicColor(callbacks)
+                        }
+                    }
+
+                    launch {
+                        viewModel.overlayViewParams.collect { params ->
+                            windowManager.updateViewLayout(it, params)
+                            lottie.resumeAnimation()
+                        }
+                    }
+
+                    launch {
+                        viewModel.overlayViewProperties.collect { properties ->
+                            it.rotation = properties.overlayViewRotation
+                            lottie.setAnimation(properties.indicatorAsset)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+private fun LottieAnimationView.addOverlayDynamicColor(colorCallbacks: List<LottieCallback>) {
+    addLottieOnCompositionLoadedListener {
+        for (callback in colorCallbacks) {
+            addValueCallback(callback.keypath, LottieProperty.COLOR_FILTER) {
+                PorterDuffColorFilter(callback.color, PorterDuff.Mode.SRC_ATOP)
+            }
+        }
+        resumeAnimation()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 647aaf3..6d0a58e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -21,9 +21,12 @@
 import android.util.Log
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
+import com.android.systemui.Flags.bpTalkback
+import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
 import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -35,7 +38,9 @@
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -49,7 +54,9 @@
 constructor(
     displayStateInteractor: DisplayStateInteractor,
     promptSelectorInteractor: PromptSelectorInteractor,
-    @Application context: Context,
+    @Application private val context: Context,
+    private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+    private val udfpsUtils: UdfpsUtils
 ) {
     /** The set of modalities available for this prompt */
     val modalities: Flow<BiometricModalities> =
@@ -69,6 +76,11 @@
     val faceIconHeight: Int =
         context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
 
+    private val _accessibilityHint = MutableSharedFlow<String>()
+
+    /** Hint for talkback directional guidance */
+    val accessibilityHint: Flow<String> = _accessibilityHint.asSharedFlow()
+
     private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
     /** If the user is currently authenticating (i.e. at least one biometric is scanning). */
@@ -516,6 +528,40 @@
         return false
     }
 
+    /** Sets the message used for UDFPS directional guidance */
+    suspend fun onAnnounceAccessibilityHint(
+        event: MotionEvent,
+        touchExplorationEnabled: Boolean,
+    ): Boolean {
+        if (bpTalkback() && modalities.first().hasUdfps && touchExplorationEnabled) {
+            // TODO(b/315184924): Remove uses of UdfpsUtils
+            val scaledTouch =
+                udfpsUtils.getTouchInNativeCoordinates(
+                    event.getPointerId(0),
+                    event,
+                    udfpsOverlayInteractor.udfpsOverlayParams.value
+                )
+            if (
+                !udfpsUtils.isWithinSensorArea(
+                    event.getPointerId(0),
+                    event,
+                    udfpsOverlayInteractor.udfpsOverlayParams.value
+                )
+            ) {
+                _accessibilityHint.emit(
+                    udfpsUtils.onTouchOutsideOfSensorArea(
+                        touchExplorationEnabled,
+                        context,
+                        scaledTouch.x,
+                        scaledTouch.y,
+                        udfpsOverlayInteractor.udfpsOverlayParams.value
+                    )
+                )
+            }
+        }
+        return false
+    }
+
     /**
      * Switch to the credential view.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
new file mode 100644
index 0000000..ce72603
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.Point
+import android.graphics.Rect
+import android.view.Gravity
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import com.airbnb.lottie.model.KeyPath
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+
+/** Models UI of the side fingerprint sensor indicator view. */
+class SideFpsOverlayViewModel
+@Inject
+constructor(
+    @Application private val applicationContext: Context,
+    biometricStatusInteractor: BiometricStatusInteractor,
+    deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
+    displayStateInteractor: DisplayStateInteractor,
+    sfpsSensorInteractor: SideFpsSensorInteractor,
+    sideFpsProgressBarViewModel: SideFpsProgressBarViewModel
+) {
+    /** Contains properties of the side fingerprint sensor indicator */
+    data class OverlayViewProperties(
+        /** The raw asset for the indicator animation */
+        val indicatorAsset: Int,
+        /** Rotation of the overlayView */
+        val overlayViewRotation: Float,
+    )
+
+    private val _lottieBounds: MutableStateFlow<Rect?> = MutableStateFlow(null)
+
+    /** Used for setting lottie bounds once the composition has loaded. */
+    fun setLottieBounds(bounds: Rect) {
+        _lottieBounds.value = bounds
+    }
+
+    private val displayRotation = displayStateInteractor.currentRotation
+    private val sensorLocation = sfpsSensorInteractor.sensorLocation
+
+    /** Default LayoutParams for the overlayView */
+    val defaultOverlayViewParams: WindowManager.LayoutParams
+        get() =
+            WindowManager.LayoutParams(
+                    WindowManager.LayoutParams.WRAP_CONTENT,
+                    WindowManager.LayoutParams.WRAP_CONTENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                    Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
+                    PixelFormat.TRANSLUCENT
+                )
+                .apply {
+                    title = TAG
+                    fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+                    gravity = Gravity.TOP or Gravity.LEFT
+                    layoutInDisplayCutoutMode =
+                        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+                    privateFlags = PRIVATE_FLAG_TRUSTED_OVERLAY or PRIVATE_FLAG_NO_MOVE_ANIMATION
+                }
+
+    private val indicatorAsset: Flow<Int> =
+        combine(displayRotation, sensorLocation) { rotation: DisplayRotation, sensorLocation ->
+                val yAligned = sensorLocation.isSensorVerticalInDefaultOrientation
+                val newAsset: Int =
+                    when (rotation) {
+                        DisplayRotation.ROTATION_0 ->
+                            if (yAligned) {
+                                R.raw.sfps_pulse
+                            } else {
+                                R.raw.sfps_pulse_landscape
+                            }
+                        DisplayRotation.ROTATION_180 ->
+                            if (yAligned) {
+                                R.raw.sfps_pulse
+                            } else {
+                                R.raw.sfps_pulse_landscape
+                            }
+                        else ->
+                            if (yAligned) {
+                                R.raw.sfps_pulse_landscape
+                            } else {
+                                R.raw.sfps_pulse
+                            }
+                    }
+                newAsset
+            }
+            .distinctUntilChanged()
+
+    private val overlayViewRotation: Flow<Float> =
+        combine(
+                displayRotation,
+                sensorLocation,
+            ) { rotation: DisplayRotation, sensorLocation ->
+                val yAligned = sensorLocation.isSensorVerticalInDefaultOrientation
+                when (rotation) {
+                    DisplayRotation.ROTATION_90 -> if (yAligned) 0f else 180f
+                    DisplayRotation.ROTATION_180 -> 180f
+                    DisplayRotation.ROTATION_270 -> if (yAligned) 180f else 0f
+                    else -> 0f
+                }
+            }
+            .distinctUntilChanged()
+
+    /** Contains properties (animation asset and view rotation) for overlayView */
+    val overlayViewProperties: Flow<OverlayViewProperties> =
+        combine(indicatorAsset, overlayViewRotation) { asset: Int, rotation: Float ->
+            OverlayViewProperties(asset, rotation)
+        }
+
+    /** LayoutParams for placement of overlayView (the side fingerprint sensor indicator view) */
+    val overlayViewParams: Flow<WindowManager.LayoutParams> =
+        combine(
+            _lottieBounds,
+            sensorLocation,
+            displayRotation,
+        ) { bounds: Rect?, sensorLocation: SideFpsSensorLocation, displayRotation: DisplayRotation
+            ->
+            val topLeft = Point(sensorLocation.left, sensorLocation.top)
+
+            if (sensorLocation.isSensorVerticalInDefaultOrientation) {
+                if (displayRotation == DisplayRotation.ROTATION_0) {
+                    topLeft.x -= bounds!!.width()
+                } else if (displayRotation == DisplayRotation.ROTATION_270) {
+                    topLeft.y -= bounds!!.height()
+                }
+            } else {
+                if (displayRotation == DisplayRotation.ROTATION_180) {
+                    topLeft.y -= bounds!!.height()
+                } else if (displayRotation == DisplayRotation.ROTATION_270) {
+                    topLeft.x -= bounds!!.width()
+                }
+            }
+            defaultOverlayViewParams.apply {
+                x = topLeft.x
+                y = topLeft.y
+            }
+        }
+
+    /** List of LottieCallbacks use for adding dynamic color to the overlayView */
+    val lottieCallbacks: Flow<List<LottieCallback>> =
+        combine(
+            biometricStatusInteractor.sfpsAuthenticationReason,
+            deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry.distinctUntilChanged(),
+            sideFpsProgressBarViewModel.isVisible,
+        ) { reason: AuthenticationReason, showIndicatorForDeviceEntry: Boolean, progressBarIsVisible
+            ->
+            val callbacks = mutableListOf<LottieCallback>()
+            if (showIndicatorForDeviceEntry) {
+                val indicatorColor =
+                    com.android.settingslib.Utils.getColorAttrDefaultColor(
+                        applicationContext,
+                        com.android.internal.R.attr.materialColorPrimaryFixed
+                    )
+                val outerRimColor =
+                    com.android.settingslib.Utils.getColorAttrDefaultColor(
+                        applicationContext,
+                        com.android.internal.R.attr.materialColorPrimaryFixedDim
+                    )
+                val chevronFill =
+                    com.android.settingslib.Utils.getColorAttrDefaultColor(
+                        applicationContext,
+                        com.android.internal.R.attr.materialColorOnPrimaryFixed
+                    )
+                callbacks.add(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
+                callbacks.add(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
+                callbacks.add(LottieCallback(KeyPath(".black", "**"), chevronFill))
+            } else {
+                if (!isDarkMode(applicationContext)) {
+                    callbacks.add(LottieCallback(KeyPath(".black", "**"), Color.WHITE))
+                }
+                for (key in listOf(".blue600", ".blue400")) {
+                    callbacks.add(
+                        LottieCallback(
+                            KeyPath(key, "**"),
+                            applicationContext.getColor(
+                                com.android.settingslib.color.R.color.settingslib_color_blue400
+                            ),
+                        )
+                    )
+                }
+            }
+            callbacks
+        }
+
+    companion object {
+        private const val TAG = "SideFpsOverlayViewModel"
+    }
+}
+
+private fun isDarkMode(context: Context): Boolean {
+    val darkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+    return darkMode == Configuration.UI_MODE_NIGHT_YES
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index 1e0e16c..c2a1d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -18,6 +18,7 @@
 
 import android.os.Build
 import android.util.Log
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
 import com.android.systemui.dagger.SysUISingleton
@@ -121,6 +122,7 @@
 
     fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     fun setSideFpsShowing(isShowing: Boolean)
 }
 
@@ -261,7 +263,9 @@
         _isBackButtonEnabled.value = isBackButtonEnabled
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     override fun setSideFpsShowing(isShowing: Boolean) {
+        SideFpsControllerRefactor.assertInLegacyMode()
         _sideFpsShowing.value = isShowing
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index aa7758f..621ca5d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.DejankUtils
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -116,9 +117,11 @@
 
     /** Allow for interaction when just about fully visible */
     val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 }
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     val sideFpsShowing: Flow<Boolean> = repository.sideFpsShowing
     private var currentUserActiveUnlockRunning = false
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /** This callback needs to be a class field so it does not get garbage collected. */
     val keyguardUpdateMonitorCallback =
         object : KeyguardUpdateMonitorCallback() {
@@ -135,7 +138,10 @@
         }
 
     init {
-        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+        // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+        if (!SideFpsControllerRefactor.isEnabled) {
+            keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+        }
         applicationScope.launch {
             trustRepository.isCurrentUserActiveUnlockRunning.collect {
                 currentUserActiveUnlockRunning = it
@@ -333,8 +339,10 @@
         repository.setPrimaryStartDisappearAnimation(runnable)
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /** Determine whether to show the side fps animation. */
     fun updateSideFpsVisibility() {
+        SideFpsControllerRefactor.assertInLegacyMode()
         val sfpsEnabled: Boolean =
             context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
         val fpsDetectionRunning: Boolean = keyguardUpdateMonitor.isFingerprintDetectionRunning
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index ac3d4b6..5dcd661 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -27,6 +27,7 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardSecurityView
 import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.bouncer.ui.BouncerViewDelegate
@@ -233,15 +234,21 @@
                             .collect { view.systemUiVisibility = it }
                     }
 
-                    launch {
-                        viewModel.shouldUpdateSideFps.collect {
-                            viewModel.updateSideFpsVisibility()
+                    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+                    if (!SideFpsControllerRefactor.isEnabled) {
+                        launch {
+                            viewModel.shouldUpdateSideFps.collect {
+                                viewModel.updateSideFpsVisibility()
+                            }
                         }
                     }
 
-                    launch {
-                        viewModel.sideFpsShowing.collect {
-                            securityContainerController.updateSideFpsVisibility(it)
+                    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+                    if (!SideFpsControllerRefactor.isEnabled) {
+                        launch {
+                            viewModel.sideFpsShowing.collect {
+                                securityContainerController.updateSideFpsVisibility(it)
+                            }
                         }
                     }
                     awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
index 649ae2fb..1c9d1f0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.bouncer.ui.viewmodel
 
 import android.view.View
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
 import com.android.systemui.bouncer.ui.BouncerView
@@ -61,9 +62,11 @@
     /** Observe whether keyguard is authenticated already. */
     val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticatedBiometrics
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /** Observe whether the side fps is showing. */
     val sideFpsShowing: Flow<Boolean> = interactor.sideFpsShowing
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /** Observe whether we should update fps is showing. */
     val shouldUpdateSideFps: Flow<Unit> =
         merge(
@@ -87,7 +90,9 @@
         interactor.onMessageShown()
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     fun updateSideFpsVisibility() {
+        SideFpsControllerRefactor.assertInLegacyMode()
         interactor.updateSideFpsVisibility()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index 7ac1cc7..9d4ed20 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -25,11 +25,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
@@ -43,6 +47,7 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.condition.ConditionalCoreStartable;
 
@@ -195,34 +200,70 @@
 
         private final ActivityStarter mActivityStarter;
         private final Context mContext;
+        private final ConfigurationController mConfigurationController;
         private final ControlsComponent mControlsComponent;
 
         private final UiEventLogger mUiEventLogger;
 
+        private final ConfigurationController.ConfigurationListener mConfigurationListener =
+                new ConfigurationController.ConfigurationListener() {
+                    @Override
+                    public void onUiModeChanged() {
+                        reloadResources();
+                    }
+                };
+
         @Inject
         DreamHomeControlsChipViewController(
                 @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
                 ActivityStarter activityStarter,
                 Context context,
+                ConfigurationController configurationController,
                 ControlsComponent controlsComponent,
                 UiEventLogger uiEventLogger) {
             super(view);
 
             mActivityStarter = activityStarter;
             mContext = context;
+            mConfigurationController = configurationController;
             mControlsComponent = controlsComponent;
             mUiEventLogger = uiEventLogger;
         }
 
         @Override
         protected void onViewAttached() {
-            mView.setImageResource(mControlsComponent.getTileImageId());
-            mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
+            reloadResources();
             mView.setOnClickListener(this::onClickHomeControls);
+            mConfigurationController.addCallback(mConfigurationListener);
         }
 
         @Override
-        protected void onViewDetached() {}
+        protected void onViewDetached() {
+            mConfigurationController.removeCallback(mConfigurationListener);
+        }
+
+        private void reloadResources() {
+            final String title = getControlsTitle();
+            if (title != null) {
+                mView.setContentDescription(title);
+            }
+            mView.setImageResource(mControlsComponent.getTileImageId());
+            mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+            final Drawable background = mView.getBackground();
+            if (background != null) {
+                background.setTintList(
+                        Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
+            }
+        }
+
+        @Nullable
+        private String getControlsTitle() {
+            try {
+                return mContext.getString(mControlsComponent.getTileTitleId());
+            } catch (Resources.NotFoundException e) {
+                return null;
+            }
+        }
 
         private void onClickHomeControls(View v) {
             if (DEBUG) Log.d(TAG, "home controls complication tapped");
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
index 08d0595..b6dcfcb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
@@ -24,9 +24,8 @@
 import android.view.LayoutInflater;
 import android.widget.ImageView;
 
-import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
 import com.android.systemui.complication.DreamHomeControlsComplication;
+import com.android.systemui.res.R;
 import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
 import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
 
@@ -98,7 +97,7 @@
         @DreamHomeControlsComplicationScope
         @Named(DREAM_HOME_CONTROLS_BACKGROUND_DRAWABLE)
         static Drawable providesHomeControlsBackground(Context context, Resources resources) {
-            final Drawable background = new DoubleShadowIconDrawable(createShadowInfo(
+            return new DoubleShadowIconDrawable(createShadowInfo(
                             resources,
                             R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius,
                             R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx,
@@ -117,11 +116,6 @@
                             R.dimen.dream_overlay_bottom_affordance_width),
                     resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset)
             );
-
-            background.setTintList(
-                    Utils.getColorAttr(context, com.android.internal.R.attr.colorSurface));
-
-            return background;
         }
 
         private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
index ae67e60..4d89a82 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt
@@ -79,6 +79,22 @@
         check(!isEnabled) { "Legacy code path not supported when $flagName is enabled." }
 
     /**
+     * Called to ensure the new code is only run when the flag is enabled. This will throw an
+     * exception if the flag is disabled to ensure that the refactor author catches issues in
+     * testing.
+     *
+     * Example usage:
+     * ```
+     * public void setSomeNewController(SomeController someController) {
+     *     SomeRefactor.assertInNewMode();
+     *     mSomeController = someController;
+     * }
+     * ````
+     */
+    inline fun assertInNewMode(isEnabled: Boolean, flagName: Any) =
+        check(isEnabled) { "New code path not supported when $flagName is disabled." }
+
+    /**
      * This will [Log.wtf] with the given message, assuming [ASSERT_TAG] is loggable at that level.
      * This means an engineer can prevent this from crashing by running the command:
      * ```
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 33dd3d9..edf9648 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -35,7 +35,7 @@
 import static android.view.WindowManager.TransitionOldType;
 import static android.view.WindowManager.TransitionType;
 
-import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER;
+import static com.android.systemui.Flags.refactorGetCurrentUser;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -609,7 +609,7 @@
         public void setCurrentUser(int userId) {
             trace("Deprecated/NOT USED: setCurrentUser userId=" + userId);
             checkPermission();
-            if (!mFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+            if (!refactorGetCurrentUser()) {
                 mKeyguardViewMediator.setCurrentUser(userId);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b7260f2..d7a1906 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -38,7 +38,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER;
+import static com.android.systemui.Flags.refactorGetCurrentUser;
 import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
@@ -141,6 +141,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.flags.SystemPropertiesHelper;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.log.SessionTracker;
@@ -616,7 +617,7 @@
         public void onUserSwitching(int userId) {
             if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
             synchronized (KeyguardViewMediator.this) {
-                if (mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+                if (refactorGetCurrentUser()) {
                     notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
                 }
                 resetKeyguardDonePendingLocked();
@@ -1319,6 +1320,7 @@
     private DeviceConfigProxy mDeviceConfig;
     private DozeParameters mDozeParameters;
     private SelectedUserInteractor mSelectedUserInteractor;
+    private KeyguardInteractor mKeyguardInteractor;
 
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -1400,7 +1402,8 @@
             Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
             SystemPropertiesHelper systemPropertiesHelper,
             Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            KeyguardInteractor keyguardInteractor) {
         mContext = context;
         mUserTracker = userTracker;
         mFalsingCollector = falsingCollector;
@@ -1441,6 +1444,7 @@
                 }));
         mDozeParameters = dozeParameters;
         mSelectedUserInteractor = selectedUserInteractor;
+        mKeyguardInteractor = keyguardInteractor;
 
         mStatusBarStateController = statusBarStateController;
         statusBarStateController.addCallback(this);
@@ -1498,7 +1502,7 @@
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
-        if (!mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+        if (!refactorGetCurrentUser()) {
             KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
         }
 
@@ -2618,6 +2622,7 @@
         setPendingLock(false); // user may have authenticated during the screen off animation
 
         handleHide();
+        mKeyguardInteractor.keyguardDoneAnimationsFinished();
         mUpdateMonitor.clearFingerprintRecognizedWhenKeyguardDone(currentUser);
         Trace.endSection();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 331d892..3925dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -52,6 +52,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
 import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule;
 import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
@@ -154,7 +155,8 @@
             Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
             SystemPropertiesHelper systemPropertiesHelper,
             Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            KeyguardInteractor keyguardInteractor) {
         return new KeyguardViewMediator(
                 context,
                 uiEventLogger,
@@ -199,7 +201,8 @@
                 dreamingToLockscreenTransitionViewModel,
                 systemPropertiesHelper,
                 wmLockscreenVisibilityManager,
-                selectedUserInteractor);
+                selectedUserInteractor,
+                keyguardInteractor);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index c090688..4d60dd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -304,14 +304,14 @@
     }
 
     private fun observeFaceAuthResettingConditions() {
-        // Clear auth status when keyguard is going away or when the user is switching or device
-        // starts going to sleep.
+        // Clear auth status when keyguard done animations finished or when the user is switching
+        // or device starts going to sleep.
         merge(
                 powerInteractor.isAsleep,
                 if (featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
                     keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE)
                 } else {
-                    keyguardRepository.isKeyguardGoingAway
+                    keyguardRepository.keyguardDoneAnimationsFinished.map { true }
                 },
                 userRepository.selectedUser.map {
                     it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 96386f3..9a13558d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -41,6 +41,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -61,6 +62,9 @@
 
     /** Provide the current status of fingerprint authentication. */
     val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+
+    /** Indicates whether to update the side fingerprint sensor indicator visibility. */
+    val shouldUpdateIndicatorVisibility: Flow<Boolean>
 }
 
 /**
@@ -256,6 +260,37 @@
             awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
         }
 
+    override val shouldUpdateIndicatorVisibility: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val sendShouldUpdateIndicatorVisibility =
+                    { shouldUpdateIndicatorVisibility: Boolean ->
+                        trySendWithFailureLogging(
+                            shouldUpdateIndicatorVisibility,
+                            TAG,
+                            "Error sending shouldUpdateIndicatorVisibility " +
+                                "$shouldUpdateIndicatorVisibility"
+                        )
+                    }
+
+                val callback =
+                    object : KeyguardUpdateMonitorCallback() {
+                        override fun onBiometricRunningStateChanged(
+                            running: Boolean,
+                            biometricSourceType: BiometricSourceType?
+                        ) {
+                            sendShouldUpdateIndicatorVisibility(true)
+                        }
+                        override fun onStrongAuthStateChanged(userId: Int) {
+                            sendShouldUpdateIndicatorVisibility(true)
+                        }
+                    }
+                sendShouldUpdateIndicatorVisibility(false)
+                keyguardUpdateMonitor.registerCallback(callback)
+                awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+            }
+            .flowOn(mainDispatcher)
+            .shareIn(scope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
     companion object {
         const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 0df7f9b..31ef100 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -192,6 +192,17 @@
     /** Observable updated when keyguardDone should be called either now or soon. */
     val keyguardDone: Flow<KeyguardDone>
 
+    /**
+     * Emits after the keyguard is done animating away.
+     *
+     * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed.
+     */
+    @Deprecated(
+        "Use KeyguardTransitionInteractor flows instead. The closest match for " +
+            "'keyguardDoneAnimationsFinished' is when the GONE transition is finished."
+    )
+    val keyguardDoneAnimationsFinished: Flow<Unit>
+
     /** Receive whether clock should be centered on lockscreen. */
     val clockShouldBeCentered: Flow<Boolean>
 
@@ -239,6 +250,17 @@
     suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone)
 
     fun setClockShouldBeCentered(shouldBeCentered: Boolean)
+
+    /**
+     * Updates signal that the keyguard done animations are finished
+     *
+     * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed.
+     */
+    @Deprecated(
+        "Use KeyguardTransitionInteractor flows instead. The closest match for " +
+            "'keyguardDoneAnimationsFinished' is when the GONE transition is finished."
+    )
+    fun keyguardDoneAnimationsFinished()
 }
 
 /** Encapsulates application state for the keyguard. */
@@ -269,6 +291,11 @@
         _keyguardDone.emit(keyguardDoneType)
     }
 
+    override val keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = MutableSharedFlow()
+    override fun keyguardDoneAnimationsFinished() {
+        keyguardDoneAnimationsFinished.tryEmit(Unit)
+    }
+
     private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
     override val animateBottomAreaDozingTransitions =
         _animateBottomAreaDozingTransitions.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
new file mode 100644
index 0000000..de15fd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -0,0 +1,92 @@
+/*
+ *   Copyright (C) 2023 The Android Open Source Project
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.Context
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Encapsulates business logic for device entry events that impact the side fingerprint sensor
+ * overlay.
+ */
+@SysUISingleton
+class DeviceEntrySideFpsOverlayInteractor
+@Inject
+constructor(
+    @Application private val context: Context,
+    deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+    alternateBouncerInteractor: AlternateBouncerInteractor,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+
+    init {
+        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
+    }
+
+    private val showIndicatorForPrimaryBouncer: Flow<Boolean> =
+        merge(
+                primaryBouncerInteractor.isShowing,
+                primaryBouncerInteractor.startingToHide,
+                primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(),
+                deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }
+            )
+            .map { shouldShowIndicatorForPrimaryBouncer() }
+
+    private val showIndicatorForAlternateBouncer: Flow<Boolean> =
+        alternateBouncerInteractor.isVisible
+
+    /**
+     * Indicates whether the primary or alternate bouncers request showing the side fingerprint
+     * sensor indicator.
+     */
+    val showIndicatorForDeviceEntry: Flow<Boolean> =
+        combine(showIndicatorForPrimaryBouncer, showIndicatorForAlternateBouncer) {
+            showForPrimaryBouncer,
+            showForAlternateBouncer ->
+            showForPrimaryBouncer || showForAlternateBouncer
+        }
+
+    private fun shouldShowIndicatorForPrimaryBouncer(): Boolean {
+        val sfpsEnabled: Boolean =
+            context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
+        val sfpsDetectionRunning = keyguardUpdateMonitor.isFingerprintDetectionRunning
+        val isUnlockingWithFpAllowed = keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed
+        return primaryBouncerInteractor.isBouncerShowing() &&
+            sfpsEnabled &&
+            sfpsDetectionRunning &&
+            isUnlockingWithFpAllowed &&
+            !primaryBouncerInteractor.isAnimatingAway()
+    }
+
+    companion object {
+        private const val TAG = "DeviceEntrySideFpsOverlayInteractor"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index defca18..21651ba2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -318,6 +318,10 @@
         repository.ambientIndicationVisible.value = isVisible
     }
 
+    fun keyguardDoneAnimationsFinished() {
+        repository.keyguardDoneAnimationsFinished()
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
index 560e4ad..9a6dca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags
 import com.android.systemui.biometrics.SideFpsController
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
@@ -46,6 +47,7 @@
     private val viewModel: SideFpsProgressBarViewModel,
     private val view: SideFpsProgressBar,
     @Application private val applicationScope: CoroutineScope,
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     private val sfpsController: dagger.Lazy<SideFpsController>,
     private val logger: SideFpsLogger,
     private val commandRegistry: CommandRegistry,
@@ -109,12 +111,14 @@
         view.updateView(visible, location, length, thickness, rotation)
         // We have to hide the SFPS indicator as the progress bar will
         // be shown at the same location
-        if (visible) {
-            logger.hidingSfpsIndicator()
-            sfpsController.get().hideIndicator()
-        } else if (fpDetectRunning) {
-            logger.showingSfpsIndicator()
-            sfpsController.get().showIndicator()
+        if (!SideFpsControllerRefactor.isEnabled) {
+            if (visible) {
+                logger.hidingSfpsIndicator()
+                sfpsController.get().hideIndicator()
+            } else if (fpDetectRunning) {
+                logger.showingSfpsIndicator()
+                sfpsController.get().showIndicator()
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index 2d0712c..1dbf1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -19,6 +19,7 @@
 import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.Point
+import androidx.annotation.VisibleForTesting
 import androidx.core.animation.doOnEnd
 import com.android.systemui.Flags
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -213,4 +214,9 @@
             }
         }
     }
+
+    @VisibleForTesting
+    fun setVisible(isVisible: Boolean) {
+        _visible.value = isVisible
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9846f4b..e660b97 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -69,7 +69,6 @@
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
@@ -81,6 +80,7 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputChannelCompat;
@@ -1113,6 +1113,7 @@
                             // Capture inputs
                             mInputMonitor.pilferPointers();
                             if (mBackAnimation != null) {
+                                mBackAnimation.onPilferPointers();
                                 // Notify FalsingManager that an intentional gesture has occurred.
                                 mFalsingManager.isFalseTouch(BACK_GESTURE);
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 6d55d05..de490a5 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -27,7 +27,6 @@
 import android.util.Log
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -43,6 +42,7 @@
 import com.android.systemui.notetask.NoteTaskEnabledKey
 import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
 import com.android.systemui.notetask.NoteTaskInfoResolver
+import com.android.systemui.res.R
 import com.android.systemui.stylus.StylusManager
 import dagger.Lazy
 import java.util.concurrent.Executor
@@ -100,7 +100,8 @@
                     isEnabled &&
                         isUserUnlocked &&
                         isDefaultNotesAppSet &&
-                        (isConfigSelected || isStylusEverUsed)
+                        isConfigSelected &&
+                        isStylusEverUsed
                 ) {
                     val contentDescription = ContentDescription.Resource(pickerNameResourceId)
                     val icon = Icon.Resource(pickerIconResourceId, contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 6c930b1..3e50dd3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -43,6 +43,9 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.animation.DisappearParameters;
 
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -50,9 +53,6 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
 /**
  * Controller for QSPanel views.
  *
@@ -232,7 +232,7 @@
             mQsTileRevealController.updateRevealedTiles(tiles);
         }
         boolean shouldChange = false;
-        if (tiles.size() == mRecords.size()) {
+        if (tiles.size() <= mRecords.size()) {
             int i = 0;
             for (QSTile tile : tiles) {
                 if (tile != mRecords.get(i).tile) {
@@ -241,6 +241,17 @@
                 }
                 i++;
             }
+
+            // If the first tiles are the same as the new ones, remove any extras.
+            if (!shouldChange) {
+                while (i < mRecords.size()) {
+                    QSPanelControllerBase.TileRecord record = mRecords.get(i);
+                    mView.removeTile(record);
+                    record.tile.removeCallback(record.callback);
+                    i++;
+                }
+                mCachedSpecs = getTilesSpecs();
+            }
         } else {
             shouldChange = true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 935d072..42bee3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -22,8 +22,8 @@
                 AconfigFlags.FLAG_QS_NEW_PIPELINE
             )
 
-        fun assertNewTilesInLegacyMode() =
-            RefactorFlagUtils.assertInLegacyMode(
+        fun assertNewTiles() =
+            RefactorFlagUtils.assertInNewMode(
                 AconfigFlags.qsNewTiles(),
                 AconfigFlags.FLAG_QS_NEW_TILES
             )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 38bbce4..17e6375 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -19,6 +19,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.systemui.accessibility.qs.QSAccessibilityModule;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
@@ -41,7 +42,7 @@
  * com.android.systemui.qs.tiles.DreamTile#TILE_SPEC})
  *
  * After, create or find an existing Module class to house the tile's binding method (e.g. {@link
- * com.android.systemui.accessibility.AccessibilityModule}). If creating a new module, add your
+ * QSAccessibilityModule}). If creating a new module, add your
  * module to the SystemUI dagger graph by including it in an appropriate module.
  */
 @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 52e49f9..382cfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -45,7 +45,7 @@
 ) : QSFactory {
 
     init {
-        QSPipelineFlagsRepository.assertNewTilesInLegacyMode()
+        QSPipelineFlagsRepository.assertNewTiles()
         for (viewModelTileSpec in tileMap.keys) {
             require(qsTileConfigProvider.hasConfig(viewModelTileSpec)) {
                 "No config for $viewModelTileSpec"
@@ -56,7 +56,7 @@
     override fun createTile(tileSpec: String): QSTile? {
         val viewModel: QSTileViewModel =
             when (val spec = TileSpec.create(tileSpec)) {
-                is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
+                is TileSpec.CustomTileSpec -> null
                 is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
                 is TileSpec.Invalid -> null
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
new file mode 100644
index 0000000..1efbfd7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.qs.tiles.impl.colorcorrection.domain
+
+import android.content.res.Resources
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [ColorCorrectionTileModel] to [QSTileState]. */
+class ColorCorrectionTileMapper
+@Inject
+constructor(
+    @Main private val resources: Resources,
+    private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<ColorCorrectionTileModel> {
+
+    override fun map(config: QSTileConfig, data: ColorCorrectionTileModel): QSTileState =
+        QSTileState.build(resources, theme, config.uiConfig) {
+            val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction)
+
+            if (data.isEnabled) {
+                activationState = QSTileState.ActivationState.ACTIVE
+                secondaryLabel = subtitleArray[2]
+            } else {
+                activationState = QSTileState.ActivationState.INACTIVE
+                secondaryLabel = subtitleArray[1]
+            }
+            contentDescription = label
+            supportedActions =
+                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
new file mode 100644
index 0000000..cd33d45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.qs.tiles.impl.colorcorrection.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Observes color correction state changes providing the [ColorCorrectionTileModel]. */
+class ColorCorrectionTileDataInteractor
+@Inject
+constructor(
+    private val colorCorrectionRepository: ColorCorrectionRepository,
+) : QSTileDataInteractor<ColorCorrectionTileModel> {
+
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>
+    ): Flow<ColorCorrectionTileModel> {
+        return colorCorrectionRepository.isEnabled(user).map { ColorCorrectionTileModel(it) }
+    }
+    override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
new file mode 100644
index 0000000..d183802
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.qs.tiles.impl.colorcorrection.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/** Handles color correction tile clicks. */
+class ColorCorrectionUserActionInteractor
+@Inject
+constructor(
+    private val colorCorrectionRepository: ColorCorrectionRepository,
+    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+) : QSTileUserActionInteractor<ColorCorrectionTileModel> {
+
+    override suspend fun handleInput(input: QSTileInput<ColorCorrectionTileModel>): Unit =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> {
+                    colorCorrectionRepository.setIsEnabled(
+                        !data.isEnabled,
+                        user,
+                    )
+                }
+                is QSTileUserAction.LongClick -> {
+                    qsTileIntentUserActionHandler.handle(
+                        action.view,
+                        Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
+                    )
+                }
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt
index e98f6db..66487e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.biometrics
 
-import android.content.Context
-import android.util.AttributeSet
-import com.android.systemui.util.wrapper.LottieViewWrapper
+package com.android.systemui.qs.tiles.impl.colorcorrection.domain.model
 
-class SideFpsLottieViewWrapper
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs)
+/**
+ * Color correction tile model.
+ *
+ * @param isEnabled is true when the color correction is enabled;
+ */
+@JvmInline value class ColorCorrectionTileModel(val isEnabled: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 756c440..0c5472f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -41,6 +41,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -49,6 +50,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
@@ -210,23 +212,33 @@
     private void notifyKeyguardChanged() {
         Trace.beginSection("KeyguardStateController#notifyKeyguardChanged");
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
+        invokeForEachCallback(Callback::onKeyguardShowingChanged);
         Trace.endSection();
     }
 
     private void notifyKeyguardFaceAuthEnabledChanged() {
+        invokeForEachCallback(Callback::onFaceEnrolledChanged);
+    }
+
+    private void invokeForEachCallback(Consumer<Callback> consumer) {
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(callback -> {
+        ArrayList<Callback> copyOfCallbacks = new ArrayList<>(mCallbacks);
+        for (int i = 0; i < copyOfCallbacks.size(); i++) {
+            Callback callback = copyOfCallbacks.get(i);
+            // Temporary fix for b/315731775, callback is null even though only non-null callbacks
+            // are added to the list by addCallback
             if (callback != null) {
-                callback.onFaceEnrolledChanged();
+                consumer.accept(callback);
+            } else {
+                mLogger.log("KeyguardStateController callback is null", LogLevel.DEBUG);
             }
-        });
+        }
     }
 
     private void notifyUnlockedChanged() {
         Trace.beginSection("KeyguardStateController#notifyUnlockedChanged");
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(Callback::onUnlockedChanged);
+        invokeForEachCallback(Callback::onUnlockedChanged);
         Trace.endSection();
     }
 
@@ -242,10 +254,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardFadingAway",
                     keyguardFadingAway ? 1 : 0);
             mKeyguardFadingAway = keyguardFadingAway;
-            ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
-            for (int i = 0; i < callbacks.size(); i++) {
-                callbacks.get(i).onKeyguardFadingAwayChanged();
-            }
+            invokeForEachCallback(Callback::onKeyguardFadingAwayChanged);
         }
     }
 
@@ -359,7 +368,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway",
                     keyguardGoingAway ? 1 : 0);
             mKeyguardGoingAway = keyguardGoingAway;
-            new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardGoingAwayChanged);
+            invokeForEachCallback(Callback::onKeyguardGoingAwayChanged);
         }
     }
 
@@ -368,7 +377,7 @@
         if (mPrimaryBouncerShowing != showing) {
             mPrimaryBouncerShowing = showing;
 
-            new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged);
+            invokeForEachCallback(Callback::onPrimaryBouncerShowingChanged);
         }
     }
 
@@ -392,13 +401,13 @@
             boolean dismissingFromTouch) {
         mDismissAmount = dismissAmount;
         mDismissingFromTouch = dismissingFromTouch;
-        new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardDismissAmountChanged);
+        invokeForEachCallback(Callback::onKeyguardDismissAmountChanged);
     }
 
     @Override
     public void setLaunchTransitionFadingAway(boolean fadingAway) {
         mLaunchTransitionFadingAway = fadingAway;
-        new ArrayList<>(mCallbacks).forEach(Callback::onLaunchTransitionFadingAwayChanged);
+        invokeForEachCallback(Callback::onLaunchTransitionFadingAwayChanged);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 886fa70..2b9ad50 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -18,8 +18,8 @@
 
 import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
 
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
@@ -364,15 +364,23 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction());
-            boolean isManagedProfile = mUserManager.isManagedProfile(
-                    intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-            if (newWorkProfile) {
-                if (!mDeviceProvisionedController.isCurrentUserSetup() && isManagedProfile) {
+            boolean newProfile = Intent.ACTION_PROFILE_ADDED.equals(intent.getAction());
+            if (newProfile) {
+                UserHandle newUserHandle = intent.getParcelableExtra(Intent.EXTRA_USER,
+                        android.os.UserHandle.class);
+                boolean isManagedProfile =
+                        mUserManager.isManagedProfile(newUserHandle.getIdentifier());
+                if (!mDeviceProvisionedController.isUserSetup(newUserHandle.getIdentifier())
+                        && isManagedProfile) {
                     Log.i(TAG, "User setup not finished when " + intent.getAction()
                             + " was received. Deferring... Managed profile? " + isManagedProfile);
                     return;
                 }
+                if (android.os.Flags.allowPrivateProfile() && isPrivateProfile(newUserHandle)) {
+                    mDeferredThemeEvaluation = true;
+                    Log.i(TAG, "Deferring theme for private profile till user setup is complete");
+                    return;
+                }
                 if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
                 reevaluateSystemTheme(true /* forceReload */);
             } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
@@ -432,7 +440,7 @@
     public void start() {
         if (DEBUG) Log.d(TAG, "Start");
         final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+        filter.addAction(Intent.ACTION_PROFILE_ADDED);
         filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
         mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
                 UserHandle.ALL);
@@ -608,6 +616,15 @@
         return new FabricatedOverlay.Builder("com.android.systemui", name, "android").build();
     }
 
+    @VisibleForTesting
+    protected boolean isPrivateProfile(UserHandle userHandle) {
+        Context usercontext = mContext.createContextAsUser(userHandle,0);
+        if (usercontext.getSystemService(UserManager.class).isPrivateProfile()) {
+            return true;
+        }
+        return false;
+    }
+
     private void createOverlays(int color) {
         boolean nightMode = isNightMode();
         mColorScheme = new ColorScheme(color, nightMode, mThemeStyle);
@@ -784,7 +801,7 @@
 
         Set<UserHandle> managedProfiles = new HashSet<>();
         for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) {
-            if (userInfo.isManagedProfile()) {
+            if (userInfo.isProfile()) {
                 managedProfiles.add(userInfo.getUserHandle());
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 3ed05aa..0fb4b43 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -2,9 +2,8 @@
 
 import android.annotation.UserIdInt
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags.refactorGetCurrentUser
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
 import com.android.systemui.user.data.repository.UserRepository
 import javax.inject.Inject
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -12,12 +11,7 @@
 
 /** Encapsulates business logic to interact the selected user */
 @SysUISingleton
-class SelectedUserInteractor
-@Inject
-constructor(
-    private val repository: UserRepository,
-    private val flags: FeatureFlagsClassic,
-) {
+class SelectedUserInteractor @Inject constructor(private val repository: UserRepository) {
 
     /** Flow providing the ID of the currently selected user. */
     val selectedUser = repository.selectedUserInfo.map { it.id }.distinctUntilChanged()
@@ -34,7 +28,7 @@
     @UserIdInt
     @JvmOverloads
     fun getSelectedUserId(bypassFlag: Boolean = false): Int {
-        return if (bypassFlag || flags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+        return if (bypassFlag || refactorGetCurrentUser()) {
             repository.getSelectedUserInfo().id
         } else {
             KeyguardUpdateMonitor.getCurrentUser()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index ea20d29..91219f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -47,12 +47,14 @@
 import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.events.ANIMATING_OUT
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -101,6 +103,12 @@
     lateinit var interactionJankMonitor: InteractionJankMonitor
     @Mock
     lateinit var vibrator: VibratorHelper
+    @Mock
+    lateinit var udfpsUtils: UdfpsUtils
+    @Mock
+    lateinit var authController: AuthController
+    @Mock
+    lateinit var selectedUserInteractor: SelectedUserInteractor
 
     private val testScope = TestScope(StandardTestDispatcher())
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
@@ -123,6 +131,7 @@
 
     private lateinit var displayRepository: FakeDisplayRepository
     private lateinit var displayStateInteractor: DisplayStateInteractor
+    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
 
     private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
 
@@ -140,6 +149,12 @@
                     displayStateRepository,
                     displayRepository,
             )
+        udfpsOverlayInteractor =
+                UdfpsOverlayInteractor(
+                        authController,
+                        selectedUserInteractor,
+                        testScope.backgroundScope,
+                )
     }
 
     @After
@@ -532,6 +547,8 @@
             displayStateInteractor,
             promptSelectorInteractor,
             context,
+            udfpsOverlayInteractor,
+            udfpsUtils
         ),
         { credentialViewModel },
         Handler(TestableLooper.get(this).looper),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 8e54eb7..4c510ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -27,6 +27,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.keyguard.logging.KeyguardLogger
+import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FeatureFlags
@@ -40,6 +41,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.util.mockito.any
+import javax.inject.Provider
 import org.junit.After
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -51,14 +53,14 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 import org.mockito.MockitoSession
 import org.mockito.quality.Strictness
-import javax.inject.Provider
+
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -246,6 +248,7 @@
     @Test
     @RunWithLooper(setAsMainLooper = true)
     fun testAnimatorRunWhenWakeAndUnlock_fingerprint() {
+        mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
         val fpsLocation = Point(5, 5)
         `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
         controller.onViewAttached()
@@ -266,6 +269,7 @@
     @Test
     @RunWithLooper(setAsMainLooper = true)
     fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() {
+        mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
         val faceLocation = Point(5, 5)
         `when`(authController.faceSensorLocation).thenReturn(faceLocation)
         controller.onViewAttached()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
new file mode 100644
index 0000000..27d93eb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.AuthenticationStateListener
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
+import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
+import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
+import android.platform.test.annotations.RequiresFlagsEnabled
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BiometricStatusRepositoryTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var biometricManager: BiometricManager
+
+    private lateinit var underTest: BiometricStatusRepository
+
+    private val testScope = TestScope(StandardTestDispatcher())
+
+    @Before
+    fun setUp() {
+        underTest = BiometricStatusRepositoryImpl(testScope.backgroundScope, biometricManager)
+    }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenBiometricPromptAuthenticationStarted() =
+        testScope.runTest {
+            val fingerprintAuthenticationReason by
+                collectLastValue(underTest.fingerprintAuthenticationReason)
+            runCurrent()
+
+            val listener = biometricManager.captureListener()
+
+            assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            listener.onAuthenticationStarted(REASON_AUTH_BP)
+            assertThat(fingerprintAuthenticationReason)
+                .isEqualTo(AuthenticationReason.BiometricPromptAuthentication)
+        }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenDeviceEntryAuthenticationStarted() =
+        testScope.runTest {
+            val fingerprintAuthenticationReason by
+                collectLastValue(underTest.fingerprintAuthenticationReason)
+            runCurrent()
+
+            val listener = biometricManager.captureListener()
+
+            assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            listener.onAuthenticationStarted(REASON_AUTH_KEYGUARD)
+            assertThat(fingerprintAuthenticationReason)
+                .isEqualTo(AuthenticationReason.DeviceEntryAuthentication)
+        }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenOtherAuthenticationStarted() =
+        testScope.runTest {
+            val fingerprintAuthenticationReason by
+                collectLastValue(underTest.fingerprintAuthenticationReason)
+            runCurrent()
+
+            val listener = biometricManager.captureListener()
+
+            assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            listener.onAuthenticationStarted(REASON_AUTH_OTHER)
+            assertThat(fingerprintAuthenticationReason)
+                .isEqualTo(AuthenticationReason.OtherAuthentication)
+        }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenSettingsAuthenticationStarted() =
+        testScope.runTest {
+            val fingerprintAuthenticationReason by
+                collectLastValue(underTest.fingerprintAuthenticationReason)
+            runCurrent()
+
+            val listener = biometricManager.captureListener()
+
+            assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            listener.onAuthenticationStarted(REASON_AUTH_SETTINGS)
+            assertThat(fingerprintAuthenticationReason)
+                .isEqualTo(AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER))
+        }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenEnrollmentAuthenticationStarted() =
+        testScope.runTest {
+            val fingerprintAuthenticationReason by
+                collectLastValue(underTest.fingerprintAuthenticationReason)
+            runCurrent()
+
+            val listener = biometricManager.captureListener()
+
+            assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            listener.onAuthenticationStarted(REASON_ENROLL_FIND_SENSOR)
+            assertThat(fingerprintAuthenticationReason)
+                .isEqualTo(
+                    AuthenticationReason.SettingsAuthentication(
+                        SettingsOperations.ENROLL_FIND_SENSOR
+                    )
+                )
+
+            listener.onAuthenticationStarted(REASON_ENROLL_ENROLLING)
+            assertThat(fingerprintAuthenticationReason)
+                .isEqualTo(
+                    AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
+                )
+        }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenAuthenticationStopped() =
+        testScope.runTest {
+            val fingerprintAuthenticationReason by
+                collectLastValue(underTest.fingerprintAuthenticationReason)
+            runCurrent()
+
+            val listener = biometricManager.captureListener()
+
+            listener.onAuthenticationStarted(REASON_AUTH_BP)
+            listener.onAuthenticationStopped()
+            assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+        }
+}
+
+private fun BiometricManager.captureListener() =
+    withArgCaptor<AuthenticationStateListener> {
+        verify(this@captureListener).registerAuthenticationStateListener(capture())
+    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
new file mode 100644
index 0000000..6978923
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain.interactor
+
+import android.app.ActivityManager
+import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.platform.test.annotations.RequiresFlagsEnabled
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BiometricStatusInteractorImplTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var activityTaskManager: ActivityTaskManager
+
+    private lateinit var biometricStatusRepository: FakeBiometricStatusRepository
+    private lateinit var underTest: BiometricStatusInteractorImpl
+
+    private val testScope = TestScope(StandardTestDispatcher())
+
+    @Before
+    fun setup() {
+        biometricStatusRepository = FakeBiometricStatusRepository()
+        underTest = BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
+    }
+
+    @Test
+    fun updatesSfpsAuthenticationReason_whenBiometricPromptAuthenticationStarted() =
+        testScope.runTest {
+            val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+            runCurrent()
+
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            assertThat(sfpsAuthenticationReason)
+                .isEqualTo(AuthenticationReason.BiometricPromptAuthentication)
+        }
+
+    @Test
+    fun doesNotUpdateSfpsAuthenticationReason_whenDeviceEntryAuthenticationStarted() =
+        testScope.runTest {
+            val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+            runCurrent()
+
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.DeviceEntryAuthentication
+            )
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+        }
+
+    @Test
+    fun updatesSfpsAuthenticationReason_whenOtherAuthenticationStarted() =
+        testScope.runTest {
+            val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+            runCurrent()
+
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.OtherAuthentication
+            )
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.OtherAuthentication)
+        }
+
+    @Test
+    fun doesNotUpdateSfpsAuthenticationReason_whenOtherSettingsAuthenticationStarted() =
+        testScope.runTest {
+            val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+            runCurrent()
+
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            `when`(activityTaskManager.getTasks(Mockito.anyInt()))
+                .thenReturn(listOf(fpSettingsTask()))
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER)
+            )
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+        }
+
+    @Test
+    fun updatesSfpsAuthenticationReason_whenEnrollmentAuthenticationStarted() =
+        testScope.runTest {
+            val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+            runCurrent()
+
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_FIND_SENSOR)
+            )
+            assertThat(sfpsAuthenticationReason)
+                .isEqualTo(
+                    AuthenticationReason.SettingsAuthentication(
+                        SettingsOperations.ENROLL_FIND_SENSOR
+                    )
+                )
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
+            )
+            assertThat(sfpsAuthenticationReason)
+                .isEqualTo(
+                    AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
+                )
+        }
+
+    @Test
+    fun updatesFingerprintAuthenticationReason_whenAuthenticationStopped() =
+        testScope.runTest {
+            val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason)
+            runCurrent()
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning)
+        }
+}
+
+private fun fpSettingsTask() = settingsTask(".biometrics.fingerprint.FingerprintSettings")
+
+private fun settingsTask(cls: String) =
+    ActivityManager.RunningTaskInfo().apply {
+        topActivity = ComponentName.createRelative("com.android.settings", cls)
+    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
index 22e3e7f..74c4313 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics.shared.model
 
+import android.hardware.fingerprint.FingerprintSensorProperties
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -35,6 +36,46 @@
     }
 
     @Test
+    fun hasUdfps() {
+        with(
+            BiometricModalities(
+                fingerprintProperties = fingerprintSensorPropertiesInternal(
+                    sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+                ).first(),
+            )
+        ) {
+            assertThat(isEmpty).isFalse()
+            assertThat(hasUdfps).isTrue()
+            assertThat(hasSfps).isFalse()
+            assertThat(hasFace).isFalse()
+            assertThat(hasFaceOnly).isFalse()
+            assertThat(hasFingerprint).isTrue()
+            assertThat(hasFingerprintOnly).isTrue()
+            assertThat(hasFaceAndFingerprint).isFalse()
+        }
+    }
+
+    @Test
+    fun hasSfps() {
+        with(
+            BiometricModalities(
+                fingerprintProperties = fingerprintSensorPropertiesInternal(
+                    sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                ).first(),
+            )
+        ) {
+            assertThat(isEmpty).isFalse()
+            assertThat(hasUdfps).isFalse()
+            assertThat(hasSfps).isTrue()
+            assertThat(hasFace).isFalse()
+            assertThat(hasFaceOnly).isFalse()
+            assertThat(hasFingerprint).isTrue()
+            assertThat(hasFingerprintOnly).isTrue()
+            assertThat(hasFaceAndFingerprint).isFalse()
+        }
+    }
+
+    @Test
     fun fingerprintOnly() {
         with(
             BiometricModalities(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
new file mode 100644
index 0000000..b4ae00d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import android.animation.Animator
+import android.app.ActivityTaskManager
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.testing.TestableLooper
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.FpsUnlockTracker
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class SideFpsOverlayViewBinderTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var activityTaskManager: ActivityTaskManager
+    @Mock private lateinit var displayManager: DisplayManager
+    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock
+    private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
+    @Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var layoutInflater: LayoutInflater
+    @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+    @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+    @Mock private lateinit var sideFpsView: View
+    @Mock private lateinit var windowManager: WindowManager
+
+    private val contextDisplayInfo = DisplayInfo()
+
+    private val bouncerRepository = FakeKeyguardBouncerRepository()
+    private val biometricSettingsRepository = FakeBiometricSettingsRepository()
+    private val biometricStatusRepository = FakeBiometricStatusRepository()
+    private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+    private val displayRepository = FakeDisplayRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
+    private val fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+
+    private lateinit var underTest: SideFpsOverlayViewBinder
+
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+    private lateinit var deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor
+    private lateinit var displayStateInteractor: DisplayStateInteractorImpl
+    private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+    private lateinit var sfpsSensorInteractor: SideFpsSensorInteractor
+
+    private lateinit var sideFpsProgressBarViewModel: SideFpsProgressBarViewModel
+
+    private lateinit var viewModel: SideFpsOverlayViewModel
+
+    private var displayWidth: Int = 0
+    private var displayHeight: Int = 0
+    private var boundsWidth: Int = 0
+    private var boundsHeight: Int = 0
+
+    private lateinit var deviceConfig: DeviceConfig
+    private lateinit var sensorLocation: SensorLocationInternal
+
+    private val testScope = TestScope(StandardTestDispatcher())
+    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+    enum class DeviceConfig {
+        X_ALIGNED,
+        Y_ALIGNED,
+    }
+
+    @Before
+    fun setup() {
+        mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+
+        allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
+
+        mContext = spy(mContext)
+
+        val resources = mContext.resources
+        whenever(mContext.display)
+            .thenReturn(
+                Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+            )
+
+        alternateBouncerInteractor =
+            AlternateBouncerInteractor(
+                mock(StatusBarStateController::class.java),
+                mock(KeyguardStateController::class.java),
+                bouncerRepository,
+                fingerprintPropertyRepository,
+                biometricSettingsRepository,
+                FakeSystemClock(),
+                keyguardUpdateMonitor,
+                testScope.backgroundScope,
+            )
+
+        biometricStatusInteractor =
+            BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
+
+        displayStateInteractor =
+            DisplayStateInteractorImpl(
+                testScope.backgroundScope,
+                mContext,
+                fakeExecutor,
+                displayStateRepository,
+                displayRepository,
+            )
+        displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+
+        primaryBouncerInteractor =
+            PrimaryBouncerInteractor(
+                bouncerRepository,
+                mock(BouncerView::class.java),
+                mock(Handler::class.java),
+                mock(KeyguardStateController::class.java),
+                mock(KeyguardSecurityModel::class.java),
+                mock(PrimaryBouncerCallbackInteractor::class.java),
+                mock(FalsingCollector::class.java),
+                mock(DismissCallbackRegistry::class.java),
+                mContext,
+                keyguardUpdateMonitor,
+                FakeTrustRepository(),
+                testScope.backgroundScope,
+                selectedUserInteractor,
+                faceAuthInteractor
+            )
+
+        deviceEntrySideFpsOverlayInteractor =
+            DeviceEntrySideFpsOverlayInteractor(
+                mContext,
+                deviceEntryFingerprintAuthRepository,
+                primaryBouncerInteractor,
+                alternateBouncerInteractor,
+                keyguardUpdateMonitor
+            )
+
+        whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+            .thenReturn(MutableStateFlow(false))
+
+        sfpsSensorInteractor =
+            SideFpsSensorInteractor(
+                mContext,
+                fingerprintPropertyRepository,
+                windowManager,
+                displayStateInteractor,
+                Optional.of(fingerprintInteractiveToAuthProvider),
+                SideFpsLogger(logcatLogBuffer("SfpsLogger"))
+            )
+
+        sideFpsProgressBarViewModel =
+            SideFpsProgressBarViewModel(
+                mContext,
+                deviceEntryFingerprintAuthRepository,
+                sfpsSensorInteractor,
+                displayStateInteractor,
+                testScope.backgroundScope,
+            )
+
+        viewModel =
+            SideFpsOverlayViewModel(
+                mContext,
+                biometricStatusInteractor,
+                deviceEntrySideFpsOverlayInteractor,
+                displayStateInteractor,
+                sfpsSensorInteractor,
+                sideFpsProgressBarViewModel
+            )
+
+        underTest =
+            SideFpsOverlayViewBinder(
+                testScope.backgroundScope,
+                mContext,
+                biometricStatusInteractor,
+                displayStateInteractor,
+                deviceEntrySideFpsOverlayInteractor,
+                fpsUnlockTracker,
+                layoutInflater,
+                sideFpsProgressBarViewModel,
+                sfpsSensorInteractor,
+                windowManager
+            )
+
+        context.addMockSystemService(DisplayManager::class.java, displayManager)
+        context.addMockSystemService(WindowManager::class.java, windowManager)
+
+        `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView)
+        `when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+            .thenReturn(mock(LottieAnimationView::class.java))
+        with(mock(ViewPropertyAnimator::class.java)) {
+            `when`(sideFpsView.animate()).thenReturn(this)
+            `when`(alpha(Mockito.anyFloat())).thenReturn(this)
+            `when`(setStartDelay(Mockito.anyLong())).thenReturn(this)
+            `when`(setDuration(Mockito.anyLong())).thenReturn(this)
+            `when`(setListener(any())).thenAnswer {
+                (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd(
+                    mock(Animator::class.java)
+                )
+                this
+            }
+        }
+    }
+
+    @Test
+    fun verifyIndicatorNotAdded_whenInRearDisplayMode() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = true
+            )
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            verify(windowManager, never()).addView(any(), any())
+        }
+    }
+
+    @Test
+    fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+            // Show primary bouncer
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            verify(windowManager).addView(any(), any())
+
+            // Hide primary bouncer
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            verify(windowManager).removeView(any())
+        }
+    }
+
+    @Test
+    fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+            // Show alternate bouncer
+            bouncerRepository.setAlternateVisible(true)
+            runCurrent()
+
+            verify(windowManager).addView(any(), any())
+
+            // Hide alternate bouncer
+            bouncerRepository.setAlternateVisible(false)
+            runCurrent()
+
+            verify(windowManager).removeView(any())
+        }
+    }
+
+    @Test
+    fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            // System server authentication started
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            runCurrent()
+
+            verify(windowManager).addView(any(), any())
+
+            // System server authentication stopped
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            runCurrent()
+
+            verify(windowManager).removeView(any())
+        }
+    }
+
+    private fun updatePrimaryBouncer(
+        isShowing: Boolean,
+        isAnimatingAway: Boolean,
+        fpsDetectionRunning: Boolean,
+        isUnlockingWithFpAllowed: Boolean,
+    ) {
+        bouncerRepository.setPrimaryShow(isShowing)
+        bouncerRepository.setPrimaryStartingToHide(false)
+        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+        bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
+
+        whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
+            .thenReturn(fpsDetectionRunning)
+        whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+            .thenReturn(isUnlockingWithFpAllowed)
+        mContext.orCreateTestableResources.addOverride(
+            R.bool.config_show_sidefps_hint_on_bouncer,
+            true
+        )
+    }
+
+    private suspend fun TestScope.setupTestConfiguration(
+        deviceConfig: DeviceConfig,
+        rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+        isInRearDisplayMode: Boolean,
+    ) {
+        this@SideFpsOverlayViewBinderTest.deviceConfig = deviceConfig
+
+        when (deviceConfig) {
+            DeviceConfig.X_ALIGNED -> {
+                displayWidth = 3000
+                displayHeight = 1500
+                boundsWidth = 200
+                boundsHeight = 100
+                sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
+            }
+            DeviceConfig.Y_ALIGNED -> {
+                displayWidth = 2500
+                displayHeight = 2000
+                boundsWidth = 100
+                boundsHeight = 200
+                sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
+            }
+        }
+
+        whenever(windowManager.maximumWindowMetrics)
+            .thenReturn(
+                WindowMetrics(
+                    Rect(0, 0, displayWidth, displayHeight),
+                    mock(WindowInsets::class.java),
+                )
+            )
+
+        contextDisplayInfo.uniqueId = DISPLAY_ID
+
+        fingerprintPropertyRepository.setProperties(
+            sensorId = 1,
+            strength = SensorStrength.STRONG,
+            sensorType = FingerprintSensorType.POWER_BUTTON,
+            sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
+        )
+
+        displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
+        displayStateRepository.setCurrentRotation(rotation)
+        displayRepository.emitDisplayChangeEvent(0)
+        underTest.start()
+        runCurrent()
+    }
+
+    companion object {
+        private const val DISPLAY_ID = "displayId"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index d06cbbb..7475235 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics.ui.viewmodel
 
 import android.content.res.Configuration
+import android.graphics.Point
 import android.hardware.biometrics.PromptInfo
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.fingerprint.FingerprintSensorProperties
@@ -25,7 +26,10 @@
 import android.view.MotionEvent
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags.FLAG_BP_TALKBACK
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
@@ -33,6 +37,7 @@
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.extractAuthenticatorTypes
 import com.android.systemui.biometrics.faceSensorPropertiesInternal
 import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
@@ -45,8 +50,10 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -77,7 +84,9 @@
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
-    @Mock private lateinit var vibrator: VibratorHelper
+    @Mock private lateinit var authController: AuthController
+    @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+    @Mock private lateinit var udfpsUtils: UdfpsUtils
 
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val testScope = TestScope()
@@ -87,6 +96,7 @@
     private lateinit var displayStateRepository: FakeDisplayStateRepository
     private lateinit var displayRepository: FakeDisplayRepository
     private lateinit var displayStateInteractor: DisplayStateInteractor
+    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
 
     private lateinit var selector: PromptSelectorInteractor
     private lateinit var viewModel: PromptViewModel
@@ -116,11 +126,24 @@
                 displayStateRepository,
                 displayRepository,
             )
+        udfpsOverlayInteractor =
+            UdfpsOverlayInteractor(
+                authController,
+                selectedUserInteractor,
+                testScope.backgroundScope
+            )
         selector =
             PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
         selector.resetPrompt()
 
-        viewModel = PromptViewModel(displayStateInteractor, selector, mContext)
+        viewModel =
+            PromptViewModel(
+                displayStateInteractor,
+                selector,
+                mContext,
+                udfpsOverlayInteractor,
+                udfpsUtils
+            )
         iconViewModel = viewModel.iconViewModel
     }
 
@@ -1153,6 +1176,29 @@
         assertThat(size).isEqualTo(PromptSize.LARGE)
     }
 
+    @Test
+    fun hint_for_talkback_guidance() = runGenericTest {
+        mSetFlagsRule.enableFlags(FLAG_BP_TALKBACK)
+        val hint by collectLastValue(viewModel.accessibilityHint)
+
+        // Touches should fall outside of sensor area
+        whenever(udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
+            .thenReturn(Point(0, 0))
+        whenever(udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
+            .thenReturn("Direction")
+
+        viewModel.onAnnounceAccessibilityHint(
+            obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
+            true
+        )
+
+        if (testCase.modalities.hasUdfps) {
+            assertThat(hint?.isNotBlank()).isTrue()
+        } else {
+            assertThat(hint.isNullOrBlank()).isTrue()
+        }
+    }
+
     /** Asserts that the selected buttons are visible now. */
     private suspend fun TestScope.assertButtonsVisible(
         tryAgain: Boolean = false,
@@ -1220,14 +1266,19 @@
                     authenticatedModality = BiometricModality.Face,
                 ),
                 TestCase(
-                    fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
+                    fingerprint =
+                        fingerprintSensorPropertiesInternal(
+                                strong = true,
+                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                            )
+                            .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
                 ),
                 TestCase(
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                                sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
@@ -1264,20 +1315,30 @@
                 TestCase(
                     face = faceSensorPropertiesInternal(strong = true).first(),
                     fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
-                    authenticatedModality = BiometricModality.Fingerprint,
-                ),
-                TestCase(
-                    face = faceSensorPropertiesInternal(strong = true).first(),
-                    fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
                     authenticatedModality = BiometricModality.Face,
                     confirmationRequested = true,
                 ),
                 TestCase(
                     face = faceSensorPropertiesInternal(strong = true).first(),
-                    fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(),
+                    fingerprint =
+                        fingerprintSensorPropertiesInternal(
+                                strong = true,
+                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                            )
+                            .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
                     confirmationRequested = true,
                 ),
+                TestCase(
+                    face = faceSensorPropertiesInternal(strong = true).first(),
+                    fingerprint =
+                        fingerprintSensorPropertiesInternal(
+                                strong = true,
+                                sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+                            )
+                            .first(),
+                    authenticatedModality = BiometricModality.Fingerprint,
+                ),
             )
     }
 }
@@ -1309,6 +1370,9 @@
             else -> false
         }
 
+    val modalities: BiometricModalities
+        get() = BiometricModalities(fingerprint, face)
+
     val authenticatedByFingerprint: Boolean
         get() = authenticatedModality == BiometricModality.Fingerprint
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
new file mode 100644
index 0000000..2267bdc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.app.ActivityTaskManager
+import android.content.res.Configuration.UI_MODE_NIGHT_NO
+import android.content.res.Configuration.UI_MODE_NIGHT_YES
+import android.graphics.Color
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.airbnb.lottie.model.KeyPath
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.settingslib.Utils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class SideFpsOverlayViewModelTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var activityTaskManager: ActivityTaskManager
+    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock
+    private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+    @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+    @Mock private lateinit var windowManager: WindowManager
+
+    private val contextDisplayInfo = DisplayInfo()
+
+    private val bouncerRepository = FakeKeyguardBouncerRepository()
+    private val biometricSettingsRepository = FakeBiometricSettingsRepository()
+    private val biometricStatusRepository = FakeBiometricStatusRepository()
+    private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+    private val displayRepository = FakeDisplayRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
+    private val fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+
+    private val indicatorColor =
+        Utils.getColorAttrDefaultColor(
+            context,
+            com.android.internal.R.attr.materialColorPrimaryFixed
+        )
+    private val outerRimColor =
+        Utils.getColorAttrDefaultColor(
+            context,
+            com.android.internal.R.attr.materialColorPrimaryFixedDim
+        )
+    private val chevronFill =
+        Utils.getColorAttrDefaultColor(
+            context,
+            com.android.internal.R.attr.materialColorOnPrimaryFixed
+        )
+    private val color_blue400 =
+        context.getColor(com.android.settingslib.color.R.color.settingslib_color_blue400)
+
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+    private lateinit var deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor
+    private lateinit var displayStateInteractor: DisplayStateInteractorImpl
+    private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+    private lateinit var sfpsSensorInteractor: SideFpsSensorInteractor
+
+    private lateinit var sideFpsProgressBarViewModel: SideFpsProgressBarViewModel
+
+    private lateinit var underTest: SideFpsOverlayViewModel
+
+    private var displayWidth: Int = 0
+    private var displayHeight: Int = 0
+    private var boundsWidth: Int = 0
+    private var boundsHeight: Int = 0
+
+    private lateinit var deviceConfig: DeviceConfig
+    private lateinit var sensorLocation: SensorLocationInternal
+
+    private val testScope = TestScope(StandardTestDispatcher())
+    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+    enum class DeviceConfig {
+        X_ALIGNED,
+        Y_ALIGNED,
+    }
+
+    @Before
+    fun setup() {
+        mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+
+        mContext = spy(mContext)
+
+        val resources = mContext.resources
+        whenever(mContext.display)
+            .thenReturn(
+                Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+            )
+
+        alternateBouncerInteractor =
+            AlternateBouncerInteractor(
+                mock(StatusBarStateController::class.java),
+                mock(KeyguardStateController::class.java),
+                bouncerRepository,
+                fingerprintPropertyRepository,
+                biometricSettingsRepository,
+                FakeSystemClock(),
+                keyguardUpdateMonitor,
+                testScope.backgroundScope,
+            )
+
+        biometricStatusInteractor =
+            BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
+
+        displayStateInteractor =
+            DisplayStateInteractorImpl(
+                testScope.backgroundScope,
+                mContext,
+                fakeExecutor,
+                displayStateRepository,
+                displayRepository,
+            )
+        displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+
+        primaryBouncerInteractor =
+            PrimaryBouncerInteractor(
+                bouncerRepository,
+                mock(BouncerView::class.java),
+                mock(Handler::class.java),
+                mock(KeyguardStateController::class.java),
+                mock(KeyguardSecurityModel::class.java),
+                mock(PrimaryBouncerCallbackInteractor::class.java),
+                mock(FalsingCollector::class.java),
+                mock(DismissCallbackRegistry::class.java),
+                mContext,
+                keyguardUpdateMonitor,
+                FakeTrustRepository(),
+                testScope.backgroundScope,
+                selectedUserInteractor,
+                faceAuthInteractor
+            )
+
+        deviceEntrySideFpsOverlayInteractor =
+            DeviceEntrySideFpsOverlayInteractor(
+                mContext,
+                deviceEntryFingerprintAuthRepository,
+                primaryBouncerInteractor,
+                alternateBouncerInteractor,
+                keyguardUpdateMonitor
+            )
+
+        whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+            .thenReturn(MutableStateFlow(false))
+
+        sfpsSensorInteractor =
+            SideFpsSensorInteractor(
+                mContext,
+                fingerprintPropertyRepository,
+                windowManager,
+                displayStateInteractor,
+                Optional.of(fingerprintInteractiveToAuthProvider),
+                SideFpsLogger(logcatLogBuffer("SfpsLogger"))
+            )
+
+        sideFpsProgressBarViewModel =
+            SideFpsProgressBarViewModel(
+                mContext,
+                deviceEntryFingerprintAuthRepository,
+                sfpsSensorInteractor,
+                displayStateInteractor,
+                testScope.backgroundScope,
+            )
+
+        underTest =
+            SideFpsOverlayViewModel(
+                mContext,
+                biometricStatusInteractor,
+                deviceEntrySideFpsOverlayInteractor,
+                displayStateInteractor,
+                sfpsSensorInteractor,
+                sideFpsProgressBarViewModel,
+            )
+    }
+
+    @Test
+    fun updatesOverlayViewProperties_onDisplayRotationChange_xAlignedSensor() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewProperties by collectLastValue(underTest.overlayViewProperties)
+
+            runCurrent()
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+        }
+    }
+
+    @Test
+    fun updatesOverlayViewProperties_onDisplayRotationChange_yAlignedSensor() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.Y_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewProperties by collectLastValue(underTest.overlayViewProperties)
+
+            runCurrent()
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+        }
+    }
+
+    @Test
+    fun updatesOverlayViewParams_onDisplayRotationChange_xAlignedSensor() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewParams by collectLastValue(underTest.overlayViewParams)
+
+            underTest.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
+            runCurrent()
+
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationX)
+            assertThat(overlayViewParams!!.y).isEqualTo(0)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(0)
+            assertThat(overlayViewParams!!.y)
+                .isEqualTo(
+                    displayHeight - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
+                )
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x)
+                .isEqualTo(
+                    displayWidth - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
+                )
+            assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
+            assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationX)
+        }
+    }
+
+    @Test
+    fun updatesOverlayViewParams_onDisplayRotationChange_yAlignedSensor() {
+        testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.Y_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewParams by collectLastValue(underTest.overlayViewParams)
+
+            underTest.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
+            runCurrent()
+
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
+            assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationY)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationY)
+            assertThat(overlayViewParams!!.y).isEqualTo(0)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(0)
+            assertThat(overlayViewParams!!.y)
+                .isEqualTo(
+                    displayHeight - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
+                )
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x)
+                .isEqualTo(
+                    displayWidth - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
+                )
+            assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
+        }
+    }
+
+    @Test
+    fun updatesLottieCallbacks_onShowIndicatorForDeviceEntry() {
+        testScope.runTest {
+            val lottieCallbacks by collectLastValue(underTest.lottieCallbacks)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".black", "**"), chevronFill))
+        }
+    }
+
+    @Test
+    fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inDarkMode() {
+        testScope.runTest {
+            val lottieCallbacks by collectLastValue(underTest.lottieCallbacks)
+            setDarkMode(true)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
+        }
+    }
+
+    @Test
+    fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inLightMode() {
+        testScope.runTest {
+            val lottieCallbacks by collectLastValue(underTest.lottieCallbacks)
+            setDarkMode(false)
+
+            biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".black", "**"), Color.WHITE))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
+        }
+    }
+
+    private fun setDarkMode(inDarkMode: Boolean) {
+        val uiMode =
+            if (inDarkMode) {
+                UI_MODE_NIGHT_YES
+            } else {
+                UI_MODE_NIGHT_NO
+            }
+
+        mContext.resources.configuration.uiMode = uiMode
+    }
+
+    private fun updatePrimaryBouncer(
+        isShowing: Boolean,
+        isAnimatingAway: Boolean,
+        fpsDetectionRunning: Boolean,
+        isUnlockingWithFpAllowed: Boolean,
+    ) {
+        bouncerRepository.setPrimaryShow(isShowing)
+        bouncerRepository.setPrimaryStartingToHide(false)
+        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+        bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
+
+        whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
+            .thenReturn(fpsDetectionRunning)
+        whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+            .thenReturn(isUnlockingWithFpAllowed)
+        mContext.orCreateTestableResources.addOverride(
+            R.bool.config_show_sidefps_hint_on_bouncer,
+            true
+        )
+    }
+
+    private suspend fun TestScope.setupTestConfiguration(
+        deviceConfig: DeviceConfig,
+        rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+        isInRearDisplayMode: Boolean,
+    ) {
+        this@SideFpsOverlayViewModelTest.deviceConfig = deviceConfig
+
+        when (deviceConfig) {
+            DeviceConfig.X_ALIGNED -> {
+                displayWidth = 3000
+                displayHeight = 1500
+                boundsWidth = 200
+                boundsHeight = 100
+                sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
+            }
+            DeviceConfig.Y_ALIGNED -> {
+                displayWidth = 2500
+                displayHeight = 2000
+                boundsWidth = 100
+                boundsHeight = 200
+                sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
+            }
+        }
+
+        whenever(windowManager.maximumWindowMetrics)
+            .thenReturn(
+                WindowMetrics(
+                    Rect(0, 0, displayWidth, displayHeight),
+                    mock(WindowInsets::class.java),
+                )
+            )
+
+        contextDisplayInfo.uniqueId = DISPLAY_ID
+
+        fingerprintPropertyRepository.setProperties(
+            sensorId = 1,
+            strength = SensorStrength.STRONG,
+            sensorType = FingerprintSensorType.POWER_BUTTON,
+            sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
+        )
+
+        displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
+
+        displayStateRepository.setCurrentRotation(rotation)
+
+        displayRepository.emitDisplayChangeEvent(0)
+        runCurrent()
+    }
+
+    companion object {
+        private const val DISPLAY_ID = "displayId"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 37a093e..dacf23a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
@@ -342,6 +343,7 @@
         assertThat(underTest.willDismissWithAction()).isFalse()
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun testSideFpsVisibility() {
         updateSideFpsVisibilityParameters(
@@ -355,6 +357,7 @@
         verify(repository).setSideFpsShowing(true)
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun testSideFpsVisibility_notVisible() {
         updateSideFpsVisibilityParameters(
@@ -368,6 +371,7 @@
         verify(repository).setSideFpsShowing(false)
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun testSideFpsVisibility_sfpsNotEnabled() {
         updateSideFpsVisibilityParameters(
@@ -381,6 +385,7 @@
         verify(repository).setSideFpsShowing(false)
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun testSideFpsVisibility_fpsDetectionNotRunning() {
         updateSideFpsVisibilityParameters(
@@ -394,6 +399,7 @@
         verify(repository).setSideFpsShowing(false)
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun testSideFpsVisibility_UnlockingWithFpNotAllowed() {
         updateSideFpsVisibilityParameters(
@@ -407,6 +413,7 @@
         verify(repository).setSideFpsShowing(false)
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Test
     fun testSideFpsVisibility_AnimatingAway() {
         updateSideFpsVisibilityParameters(
@@ -492,6 +499,7 @@
         isUnlockingWithFpAllowed: Boolean,
         isAnimatingAway: Boolean
     ) {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         whenever(repository.primaryBouncerShow.value).thenReturn(isVisible)
         resources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, sfpsEnabled)
         whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 2207180..07c980b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -21,14 +21,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
@@ -48,6 +47,7 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,9 +71,6 @@
     private DreamOverlayStateController mDreamOverlayStateController;
 
     @Mock
-    private Context mContext;
-
-    @Mock
     private Resources mResources;
 
     @Mock
@@ -100,6 +97,9 @@
     @Mock
     private UiEventLogger mUiEventLogger;
 
+    @Mock
+    private ConfigurationController mConfigurationController;
+
     @Captor
     private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
 
@@ -109,7 +109,8 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        when(mContext.getString(anyInt())).thenReturn("");
+        mContext.ensureTestableResources();
+
         when(mControlsComponent.getControlsController()).thenReturn(
                 Optional.of(mControlsController));
         when(mControlsComponent.getControlsListingController()).thenReturn(
@@ -225,6 +226,7 @@
                         mHomeControlsView,
                         mActivityStarter,
                         mContext,
+                        mConfigurationController,
                         mControlsComponent,
                         mUiEventLogger);
         viewController.onViewAttached();
@@ -237,6 +239,24 @@
         verify(mUiEventLogger).log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED);
     }
 
+    @Test
+    public void testUnregistersConfigurationCallback() {
+        final DreamHomeControlsComplication.DreamHomeControlsChipViewController viewController =
+                new DreamHomeControlsComplication.DreamHomeControlsChipViewController(
+                        mHomeControlsView,
+                        mActivityStarter,
+                        mContext,
+                        mConfigurationController,
+                        mControlsComponent,
+                        mUiEventLogger);
+        viewController.onViewAttached();
+        verify(mConfigurationController).addCallback(any());
+        verify(mConfigurationController, never()).removeCallback(any());
+
+        viewController.onViewDetached();
+        verify(mConfigurationController).removeCallback(any());
+    }
+
     private void setHaveFavorites(boolean value) {
         final List<StructureInfo> favorites = mock(List.class);
         when(favorites.isEmpty()).thenReturn(!value);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index ae5f625..b38c9ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -24,6 +24,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER;
 import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
 import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
 import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -93,6 +94,7 @@
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.flags.SystemPropertiesHelper;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -190,6 +192,7 @@
     private @Mock ShadeInteractor mShadeInteractor;
     private @Mock ShadeWindowLogger mShadeWindowLogger;
     private @Mock SelectedUserInteractor mSelectedUserInteractor;
+    private @Mock KeyguardInteractor mKeyguardInteractor;
     private @Captor ArgumentCaptor<KeyguardStateController.Callback>
             mKeyguardStateControllerCallback;
     private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -264,7 +267,7 @@
                 mSceneContainerFlags);
         mFeatureFlags = new FakeFeatureFlags();
         mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
-        mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true);
+        mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
 
         DejankUtils.setImmediate(true);
 
@@ -1131,7 +1134,8 @@
                 () -> mDreamingToLockscreenTransitionViewModel,
                 mSystemPropertiesHelper,
                 () -> mock(WindowManagerLockscreenVisibilityManager.class),
-                mSelectedUserInteractor);
+                mSelectedUserInteractor,
+                mKeyguardInteractor);
         mViewMediator.start();
 
         mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
new file mode 100644
index 0000000..70d3f81
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Handler
+import android.platform.test.annotations.RequiresFlagsEnabled
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
+
+    private val bouncerRepository = FakeKeyguardBouncerRepository()
+    private val biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+    private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+
+    private lateinit var underTest: DeviceEntrySideFpsOverlayInteractor
+
+    private val testScope = TestScope(StandardTestDispatcher())
+
+    @Before
+    fun setup() {
+        primaryBouncerInteractor =
+            PrimaryBouncerInteractor(
+                bouncerRepository,
+                mock(BouncerView::class.java),
+                mock(Handler::class.java),
+                mock(KeyguardStateController::class.java),
+                mock(KeyguardSecurityModel::class.java),
+                mock(PrimaryBouncerCallbackInteractor::class.java),
+                mock(FalsingCollector::class.java),
+                mock(DismissCallbackRegistry::class.java),
+                mContext,
+                keyguardUpdateMonitor,
+                FakeTrustRepository(),
+                testScope.backgroundScope,
+                mSelectedUserInteractor,
+                faceAuthInteractor
+            )
+        alternateBouncerInteractor =
+            AlternateBouncerInteractor(
+                mock(StatusBarStateController::class.java),
+                mock(KeyguardStateController::class.java),
+                bouncerRepository,
+                FakeFingerprintPropertyRepository(),
+                biometricSettingsRepository,
+                FakeSystemClock(),
+                keyguardUpdateMonitor,
+                testScope.backgroundScope,
+            )
+        underTest =
+            DeviceEntrySideFpsOverlayInteractor(
+                mContext,
+                FakeDeviceEntryFingerprintAuthRepository(),
+                primaryBouncerInteractor,
+                alternateBouncerInteractor,
+                keyguardUpdateMonitor
+            )
+    }
+
+    @Test
+    fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerShowing() =
+        testScope.runTest {
+            val showIndicatorForDeviceEntry by
+                collectLastValue(underTest.showIndicatorForDeviceEntry)
+            runCurrent()
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(true)
+        }
+
+    @Test
+    fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerHidden() =
+        testScope.runTest {
+            val showIndicatorForDeviceEntry by
+                collectLastValue(underTest.showIndicatorForDeviceEntry)
+            runCurrent()
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+        }
+
+    @Test
+    fun updatesShowIndicatorForDeviceEntry_fromPrimaryBouncer_whenFpsDetectionNotRunning() {
+        testScope.runTest {
+            val showIndicatorForDeviceEntry by
+                collectLastValue(underTest.showIndicatorForDeviceEntry)
+            runCurrent()
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = false,
+                isUnlockingWithFpAllowed = true
+            )
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun updatesShowIndicatorForDeviceEntry_fromPrimaryBouncer_onUnlockingWithFpDisallowed() {
+        testScope.runTest {
+            val showIndicatorForDeviceEntry by
+                collectLastValue(underTest.showIndicatorForDeviceEntry)
+            runCurrent()
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = false
+            )
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerAnimatingAway() {
+        testScope.runTest {
+            val showIndicatorForDeviceEntry by
+                collectLastValue(underTest.showIndicatorForDeviceEntry)
+            runCurrent()
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = true,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun updatesShowIndicatorForDeviceEntry_onAlternateBouncerRequest() =
+        testScope.runTest {
+            val showIndicatorForDeviceEntry by
+                collectLastValue(underTest.showIndicatorForDeviceEntry)
+            runCurrent()
+
+            bouncerRepository.setAlternateVisible(true)
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(true)
+
+            bouncerRepository.setAlternateVisible(false)
+            assertThat(showIndicatorForDeviceEntry).isEqualTo(false)
+        }
+
+    private fun updatePrimaryBouncer(
+        isShowing: Boolean,
+        isAnimatingAway: Boolean,
+        fpsDetectionRunning: Boolean,
+        isUnlockingWithFpAllowed: Boolean,
+    ) {
+        bouncerRepository.setPrimaryShow(isShowing)
+        bouncerRepository.setPrimaryStartingToHide(false)
+        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+        bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation)
+
+        whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
+            .thenReturn(fpsDetectionRunning)
+        whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+            .thenReturn(isUnlockingWithFpAllowed)
+        mContext.orCreateTestableResources.addOverride(
+            R.bool.config_show_sidefps_hint_on_bouncer,
+            true
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 8dea57c..8e81185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -20,8 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,11 +44,7 @@
 class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorTestCase() {
     private lateinit var underTest: FromPrimaryBouncerTransitionInteractor
 
-    private val mSelectedUserInteractor =
-        SelectedUserInteractor(
-            FakeUserRepository(),
-            FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) }
-        )
+    private val mSelectedUserInteractor = SelectedUserInteractor(FakeUserRepository())
 
     // Override the fromPrimaryBouncerTransitionInteractor provider from the superclass so our
     // underTest interactor is provided to any classes that need it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index 119ffd2..ebd34de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -28,7 +28,6 @@
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import com.android.dx.mockito.inline.extended.ExtendedMockito
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -40,6 +39,7 @@
 import com.android.systemui.notetask.NoteTaskController
 import com.android.systemui.notetask.NoteTaskEntryPoint
 import com.android.systemui.notetask.NoteTaskInfoResolver
+import com.android.systemui.res.R
 import com.android.systemui.stylus.StylusManager
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -134,12 +134,9 @@
     // region lockScreenState
     @Test
     fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest {
-        TestConfig()
-            .setStylusEverUsed(true)
-            .setUserUnlocked(true)
-            .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())
-
         val underTest = createUnderTest()
+        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(underTest)
+
         val actual by collectLastValue(underTest.lockScreenState)
 
         assertThat(actual).isEqualTo(createLockScreenStateVisible())
@@ -148,10 +145,11 @@
     @Test
     fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() =
         runTest {
+            val underTest = createUnderTest()
             TestConfig()
                 .setStylusEverUsed(true)
                 .setUserUnlocked(true)
-                .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())
+                .setConfigSelections(underTest)
             whenever(
                     roleManager.getRoleHoldersAsUser(
                         eq(RoleManager.ROLE_NOTES),
@@ -160,7 +158,6 @@
                 )
                 .thenReturn(emptyList())
 
-            val underTest = createUnderTest()
             val actual by collectLastValue(underTest.lockScreenState)
 
             assertThat(actual).isEqualTo(LockScreenState.Hidden)
@@ -168,12 +165,9 @@
 
     @Test
     fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest {
-        TestConfig()
-            .setStylusEverUsed(false)
-            .setUserUnlocked(true)
-            .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())
-
         val underTest = createUnderTest()
+        TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(underTest)
+
         val actual by collectLastValue(underTest.lockScreenState)
 
         assertThat(actual).isEqualTo(LockScreenState.Hidden)
@@ -181,25 +175,22 @@
 
     @Test
     fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest {
-        TestConfig()
-            .setStylusEverUsed(true)
-            .setUserUnlocked(false)
-            .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())
-
         val underTest = createUnderTest()
+        TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(underTest)
+
         val actual by collectLastValue(underTest.lockScreenState)
 
         assertThat(actual).isEqualTo(LockScreenState.Hidden)
     }
 
     @Test
-    fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitVisible() = runTest {
+    fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitHidden() = runTest {
         TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections()
 
         val underTest = createUnderTest()
         val actual by collectLastValue(underTest.lockScreenState)
 
-        assertThat(actual).isEqualTo(createLockScreenStateVisible())
+        assertThat(actual).isEqualTo(LockScreenState.Hidden)
     }
 
     @Test
@@ -223,13 +214,13 @@
     }
 
     @Test
-    fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitVisible() = runTest {
+    fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitHidden() = runTest {
         TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock())
 
         val underTest = createUnderTest()
         val actual by collectLastValue(underTest.lockScreenState)
 
-        assertThat(actual).isEqualTo(createLockScreenStateVisible())
+        assertThat(actual).isEqualTo(LockScreenState.Hidden)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index b825c08..018fa9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -391,7 +391,7 @@
     }
 
     @Test
-    public void setTiles_differentTiles_allTilesRemovedAndNewTilesAdded() {
+    public void setTiles_differentTiles_extraTileRemoved() {
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
 
@@ -400,8 +400,8 @@
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile));
         mController.setTiles();
 
-        verify(mQSPanel, times(2)).removeTile(any());
-        verify(mQSPanel).addTile(any());
+        verify(mQSPanel, times(1)).removeTile(any());
+        verify(mQSPanel, never()).addTile(any());
     }
 
     @Test
@@ -418,7 +418,7 @@
     }
 
     @Test
-    public void setTiles_sameTilesDifferentOrder_removesAndReadds() {
+    public void setTiles_sameTilesDifferentOrder_removesAndReads() {
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 1123688..b58a41c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.theme;
 
+import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -90,6 +91,9 @@
 
     private static final int USER_SYSTEM = UserHandle.USER_SYSTEM;
     private static final int USER_SECONDARY = 10;
+    private static final UserHandle MANAGED_USER_HANDLE = UserHandle.of(100);
+    private static final UserHandle PRIVATE_USER_HANDLE = UserHandle.of(101);
+
     @Mock
     private JavaAdapter mJavaAdapter;
     @Mock
@@ -174,6 +178,14 @@
                                 Integer.toHexString(mColorScheme.getSeed() | 0xff000000)));
                 return overlay;
             }
+
+            @VisibleForTesting
+            protected boolean isPrivateProfile(UserHandle userHandle) {
+                if (userHandle.getIdentifier() == PRIVATE_USER_HANDLE.getIdentifier()) {
+                    return true;
+                }
+                return false;
+            }
         };
 
         mWakefulnessLifecycle.dispatchFinishedWakingUp();
@@ -675,7 +687,8 @@
     @Test
     public void onProfileAdded_setsTheme() {
         mBroadcastReceiver.getValue().onReceive(null,
-                new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+                new Intent(Intent.ACTION_PROFILE_ADDED)
+                        .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE));
         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
@@ -684,7 +697,8 @@
         reset(mDeviceProvisionedController);
         when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
         mBroadcastReceiver.getValue().onReceive(null,
-                new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+                new Intent(Intent.ACTION_PROFILE_ADDED)
+                        .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE));
         verify(mThemeOverlayApplier)
                 .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
@@ -694,12 +708,26 @@
         reset(mDeviceProvisionedController);
         when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
         mBroadcastReceiver.getValue().onReceive(null,
-                new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+                new Intent(Intent.ACTION_PROFILE_ADDED)
+                        .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE));
         verify(mThemeOverlayApplier, never())
                 .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
+    public void onPrivateProfileAdded_ignoresUntilStartComplete() {
+        mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+        reset(mDeviceProvisionedController);
+        when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
+        mBroadcastReceiver.getValue().onReceive(null,
+                (new Intent(Intent.ACTION_PROFILE_ADDED))
+                        .putExtra(Intent.EXTRA_USER, PRIVATE_USER_HANDLE));
+        verify(mThemeOverlayApplier, never())
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+    }
+
+
+    @Test
     public void onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted() {
         // By default, on setup() we make this controller return that the user finished setup
         // wizard. This test on the other hand, is testing the setup flow.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
index 60fe7d2..140e919 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
@@ -2,9 +2,8 @@
 
 import android.content.pm.UserInfo
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
@@ -24,15 +23,12 @@
     @Before
     fun setUp() {
         userRepository.setUserInfos(USER_INFOS)
-        underTest =
-            SelectedUserInteractor(
-                userRepository,
-                FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) }
-            )
+        underTest = SelectedUserInteractor(userRepository)
     }
 
     @Test
     fun getSelectedUserIdReturnsId() {
+        mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER)
         runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
 
         val actualId = underTest.getSelectedUserId()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt
new file mode 100644
index 0000000..607a4f3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.data.repository
+
+import android.os.UserHandle
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeColorCorrectionRepository : ColorCorrectionRepository {
+    private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>()
+
+    override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> {
+        return getFlow(userHandle.identifier)
+    }
+
+    override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean {
+        getFlow(userHandle.identifier).value = isEnabled
+        return true
+    }
+
+    /** initializes the flow if already not */
+    private fun getFlow(userId: Int): MutableStateFlow<Boolean> {
+        return userMap.getOrPut(userId) { MutableStateFlow(false) }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
new file mode 100644
index 0000000..1c8bd3b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeBiometricStatusRepository : BiometricStatusRepository {
+    private val _fingerprintAuthenticationReason =
+        MutableStateFlow<AuthenticationReason>(AuthenticationReason.NotRunning)
+    override val fingerprintAuthenticationReason: StateFlow<AuthenticationReason> =
+        _fingerprintAuthenticationReason.asStateFlow()
+
+    fun setFingerprintAuthenticationReason(reason: AuthenticationReason) {
+        _fingerprintAuthenticationReason.value = reason
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index f84481c..ff5179a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.bouncer.data.repository
 
+import com.android.systemui.biometrics.shared.SideFpsControllerRefactor
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
 import com.android.systemui.dagger.SysUISingleton
@@ -113,7 +114,9 @@
         _isBackButtonEnabled.value = isBackButtonEnabled
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     override fun setSideFpsShowing(isShowing: Boolean) {
+        SideFpsControllerRefactor.assertInLegacyMode()
         _sideFpsShowing.value = isShowing
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index c9160ef..1d44929 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -54,6 +54,11 @@
     private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null)
     override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
         get() = _authenticationStatus.filterNotNull()
+
+    private var _shouldUpdateIndicatorVisibility = MutableStateFlow(false)
+    override val shouldUpdateIndicatorVisibility: Flow<Boolean>
+        get() = _shouldUpdateIndicatorVisibility
+
     fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) {
         _authenticationStatus.value = status
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index c5d745a..4200f05 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -41,6 +41,10 @@
     private val _deferKeyguardDone: MutableSharedFlow<KeyguardDone> = MutableSharedFlow()
     override val keyguardDone: Flow<KeyguardDone> = _deferKeyguardDone
 
+    private val _keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> =
+        MutableSharedFlow(extraBufferCapacity = 1)
+    override val keyguardDoneAnimationsFinished: Flow<Unit> = _keyguardDoneAnimationsFinished
+
     private val _clockShouldBeCentered = MutableStateFlow<Boolean>(true)
     override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered
 
@@ -176,6 +180,10 @@
         _deferKeyguardDone.emit(timing)
     }
 
+    override fun keyguardDoneAnimationsFinished() {
+        _keyguardDoneAnimationsFinished.tryEmit(Unit)
+    }
+
     override fun setClockShouldBeCentered(shouldBeCentered: Boolean) {
         _clockShouldBeCentered.value = shouldBeCentered
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 3d8ae1e..3cabf0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -27,8 +27,6 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -57,12 +55,6 @@
         keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
         bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
         keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(KeyguardUpdateMonitor::class.java),
-        featureFlags: FakeFeatureFlagsClassic =
-            FakeFeatureFlagsClassic().apply {
-                set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, true)
-                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-                set(Flags.REFACTOR_GETCURRENTUSER, true)
-            },
         powerRepository: FakePowerRepository = FakePowerRepository(),
         userRepository: FakeUserRepository = FakeUserRepository(),
     ): WithDependencies {
@@ -98,8 +90,7 @@
             PowerInteractorFactory.create(
                 repository = powerRepository,
             )
-        val selectedUserInteractor =
-            SelectedUserInteractor(repository = userRepository, flags = featureFlags)
+        val selectedUserInteractor = SelectedUserInteractor(repository = userRepository)
         return WithDependencies(
             trustRepository = trustRepository,
             keyguardRepository = keyguardRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt
index e98f6db..0357036 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.biometrics
 
-import android.content.Context
-import android.util.AttributeSet
-import com.android.systemui.util.wrapper.LottieViewWrapper
+package com.android.systemui.qs.tiles.impl.colorcorrection
 
-class SideFpsLottieViewWrapper
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs)
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsColorCorrectionTileConfig by
+    Kosmos.Fixture { QSAccessibilityModule.provideColorCorrectionTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index d78bcb9..0b41926 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -387,7 +387,7 @@
     }
 
     fun selectedUserInteractor(): SelectedUserInteractor {
-        return SelectedUserInteractor(userRepository, featureFlags)
+        return SelectedUserInteractor(userRepository)
     }
 
     fun bouncerActionButtonInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt
index 427f92a..89672f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt
@@ -16,9 +16,7 @@
 
 package com.android.systemui.user.domain.interactor
 
-import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.user.data.repository.userRepository
 
-val Kosmos.selectedUserInteractor by
-    Kosmos.Fixture { SelectedUserInteractor(userRepository, featureFlagsClassic) }
+val Kosmos.selectedUserInteractor by Kosmos.Fixture { SelectedUserInteractor(userRepository) }
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index b8cf13b..e2488a5 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -19,6 +19,9 @@
     defaults: [
         "platform_service_defaults",
     ],
+    lint: {
+        error_checks: ["MissingPermissionAnnotation"],
+    },
     srcs: [
         ":services.accessibility-sources",
         "//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc",
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 97d36d4..1d73843 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -118,6 +118,7 @@
  * This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
  * It is responsible for behavior common to both types of clients.
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub
         implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter,
         FingerprintGestureDispatcher.FingerprintGestureClient {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9ddc35a..abcd8e2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -57,6 +57,7 @@
  *
  * NOTE: This class has to be created and poked only from the main thread.
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
 
     private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index edb41639..3d8d7b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -195,6 +195,7 @@
  * event dispatch for {@link AccessibilityEvent}s generated across all processes
  * on the device. Events are dispatched to {@link AccessibilityService}s.
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 public class AccessibilityManagerService extends IAccessibilityManager.Stub
         implements AbstractAccessibilityServiceConnection.SystemSupport,
         AccessibilityUserState.ServiceInfoChangeListener,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 40ca694..5ebe161 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -65,6 +65,7 @@
  * passed to the service it represents as soon it is bound. It also serves as the
  * connection for the service.
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
     private static final String LOG_TAG = "AccessibilityServiceConnection";
 
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index a525e7c..b119d7d 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -34,6 +34,7 @@
  * If we are stripping and/or replacing the actions from a window, we need to intercept the
  * nodes heading back to the service and swap out the actions.
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 public class ActionReplacingCallback extends IAccessibilityInteractionConnectionCallback.Stub {
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "ActionReplacingCallback";
diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
index c9ec16e..e10e87c 100644
--- a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
@@ -33,6 +33,7 @@
 /**
  * Encapsulate fingerprint gesture logic
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub
         implements Handler.Callback{
     private static final int MSG_REGISTER = 1;
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index ab01fc3..6aa4702 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -64,6 +64,7 @@
  *
  * TODO(241429275): Initialize this when a proxy is registered.
  */
+@SuppressWarnings("MissingPermissionAnnotation")
 public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
     private static final String LOG_TAG = "ProxyAccessibilityServiceConnection";
 
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 53c629a..f69104d 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -245,6 +245,7 @@
         }
     }
 
+    @SuppressWarnings("MissingPermissionAnnotation")
     private class UiAutomationService extends AbstractAccessibilityServiceConnection {
         private final Handler mMainHandler;
 
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index e6af54b..e11c36a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -922,6 +922,7 @@
         disableWindowMagnification(displayId, true);
     }
 
+    @SuppressWarnings("MissingPermissionAnnotation")
     private class ConnectionCallback extends IMagnificationConnectionCallback.Stub implements
             IBinder.DeathRecipient {
         private boolean mExpiredDeathRecipient = false;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index c63784a..db5b313 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -246,6 +246,7 @@
         return new RemoteAnimationCallback(callback, trace);
     }
 
+    @SuppressWarnings("MissingPermissionAnnotation")
     private static class RemoteAnimationCallback extends
             IRemoteMagnificationAnimationCallback.Stub {
         private final MagnificationAnimationCallback mCallback;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 77a5e3d..a4b2896 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -72,6 +72,7 @@
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -559,10 +560,11 @@
                 onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
             } else if (provider.maskedBySuspendedPackage) {
                 showBadge = mUserManager.hasBadge(appUserId);
-                final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
+                final UserPackage suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
                         appInfo.packageName, appUserId);
                 // TODO(b/281839596): don't rely on platform always meaning suspended by admin.
-                if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+                if (suspendingPackage != null
+                        && PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) {
                     onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
                             appUserId, true);
                 } else {
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ab678d9..b5130a1 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -27,3 +27,10 @@
   description: "Mitigation for view state reset to empty causing no save dialog to show issue"
   bug: "297976948"
 }
+
+flag {
+  name: "include_invisible_view_group_in_assist_structure"
+  namespace: "autofill"
+  description: "Mitigation for autofill providers miscalculating view visibility"
+  bug: "291795358"
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 23e7ce6..9fdf5c2 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -92,9 +92,10 @@
                     int userId = getNextIntArgRequired();
                     String packageName = getNextArgRequired();
                     String address = getNextArgRequired();
+                    String deviceProfile = getNextArg();
                     final MacAddress macAddress = MacAddress.fromString(address);
                     mService.createNewAssociation(userId, packageName, macAddress,
-                            null, null, false);
+                            null, deviceProfile, false);
                 }
                 break;
 
@@ -350,7 +351,7 @@
         pw.println("      Print this help text.");
         pw.println("  list USER_ID");
         pw.println("      List all Associations for a user.");
-        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS");
+        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]");
         pw.println("      Create a new Association.");
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 8dc6537..0d5cdcb 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -439,6 +439,12 @@
             if (associationInfo == null) {
                 throw new IllegalArgumentException("No association with ID " + associationId);
             }
+            if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
+                    .contains(associationInfo.getDeviceProfile())
+                    && Flags.persistentDeviceIdApi()) {
+                throw new IllegalArgumentException("Unsupported CDM Association device profile "
+                        + associationInfo.getDeviceProfile() + " for virtual device creation.");
+            }
             Objects.requireNonNull(params);
             Objects.requireNonNull(activityListener);
             Objects.requireNonNull(soundEffectListener);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a0ccbf3a..f5a80d8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -200,6 +200,7 @@
         "notification_flags_lib",
         "biometrics_flags_lib",
         "am_flags_lib",
+        "com_android_systemui_shared_flags_lib",
         "com_android_wm_shell_flags_lib",
         "com.android.server.utils_aconfig-java",
         "service-jobscheduler-deviceidle.flags-aconfig-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 136692e..cac2efb 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -289,11 +289,11 @@
      *
      * @param suspendedPackage The package that has been suspended.
      * @param userId The user for which to check.
-     * @return Name of the package that suspended the given package. Returns {@code null} if the
-     * given package is not currently suspended and the platform package name - i.e.
-     * {@code "android"} - if the package was suspended by a device admin.
+     * @return User id and package name of the package that suspended the given package. Returns
+     * {@code null} if the given package is not currently suspended and the platform package name
+     * - i.e. {@code "android"} - if the package was suspended by a device admin.
      */
-    public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+    public abstract UserPackage getSuspendingPackage(String suspendedPackage, int userId);
 
     /**
      * Suspend or unsuspend packages upon admin request.
@@ -312,13 +312,13 @@
      * suspended application.
      *
      * @param suspendedPackage The package that has been suspended.
-     * @param suspendingPackage
+     * @param suspendingPackage The package responsible for suspension.
      * @param userId The user for which to check.
      * @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
      */
     @Nullable
     public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
-            String suspendingPackage, int userId);
+            UserPackage suspendingPackage, int userId);
 
     /**
      * Gets any distraction flags set via
@@ -1168,14 +1168,14 @@
     public abstract void clearBlockUninstallForUser(@UserIdInt int userId);
 
     /**
-     * Unsuspends all packages suspended by the given package for the user.
+     * Unsuspends all packages suspended by an admin for the user.
      */
-    public abstract void unsuspendForSuspendingPackage(String suspendingPackage, int userId);
+    public abstract void unsuspendAdminSuspendedPackages(int userId);
 
     /**
-     * Returns {@code true} if the package is suspending any packages for the user.
+     * Returns {@code true} if an admin is suspending any packages for the user.
      */
-    public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
+    public abstract boolean isAdminSuspendingAnyPackages(int userId);
 
     /**
      * Register to listen for loading progress of an installed package.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 33726d1..5a44ac8 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -974,6 +974,7 @@
                 unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
             } break;
             case "get": {
+                final int opts = parseOptions(shell);
                 final String key = shell.getNextArg();
                 if (key == null) {
                     pw.println("No property specified");
@@ -1007,11 +1008,17 @@
                         break;
                     case "current_now":
                         if (batteryServiceSupportCurrentAdbCommand()) {
+                            if ((opts & OPTION_FORCE_UPDATE) != 0) {
+                                updateHealthInfo();
+                            }
                             pw.println(mHealthInfo.batteryCurrentMicroamps);
                         }
                         break;
                     case "current_average":
                         if (batteryServiceSupportCurrentAdbCommand()) {
+                            if ((opts & OPTION_FORCE_UPDATE) != 0) {
+                                updateHealthInfo();
+                            }
                             pw.println(mHealthInfo.batteryCurrentAverageMicroamps);
                         }
                         break;
@@ -1125,6 +1132,14 @@
         return 0;
     }
 
+    private void updateHealthInfo() {
+        try {
+            mHealthServiceWrapper.scheduleUpdate();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to update health service data.", e);
+        }
+    }
+
     private void setChargerAcOnline(boolean online, boolean forceUpdate) {
         if (!mUpdatesStopped) {
             copyV1Battery(mLastHealthInfo, mHealthInfo);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 21b2d32..2ee39c57 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -421,6 +421,7 @@
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
@@ -468,7 +469,6 @@
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.SELinuxUtil;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0629e637..dafea9a 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -36,6 +36,7 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.biometrics.AuthenticationStateListener;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.ComponentInfoInternal;
@@ -385,6 +386,26 @@
         }
 
         @Override
+        public void registerAuthenticationStateListener(AuthenticationStateListener listener)
+                throws RemoteException {
+            checkInternalPermission();
+            final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+            if (fingerprintService != null) {
+                fingerprintService.registerAuthenticationStateListener(listener);
+            }
+        }
+
+        @Override
+        public void unregisterAuthenticationStateListener(AuthenticationStateListener listener)
+                throws RemoteException {
+            checkInternalPermission();
+            final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+            if (fingerprintService != null) {
+                fingerprintService.unregisterAuthenticationStateListener(listener);
+            }
+        }
+
+        @Override
         public void invalidateAuthenticatorIds(int userId, int fromSensorId,
                 IInvalidationCallback callback) throws RemoteException {
             checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index a47135f..f9568ea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -27,7 +27,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.security.KeyStore;
@@ -430,19 +430,19 @@
         return mLockoutTracker;
     }
 
-    protected int getShowOverlayReason() {
+    protected int getRequestReason() {
         if (isKeyguard()) {
-            return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+            return BiometricRequestConstants.REASON_AUTH_KEYGUARD;
         } else if (isBiometricPrompt()) {
             // BP reason always takes precedent over settings, since callers from within
             // settings can always invoke BP.
-            return BiometricOverlayConstants.REASON_AUTH_BP;
+            return BiometricRequestConstants.REASON_AUTH_BP;
         } else if (isSettings()) {
             // This is pretty much only for FingerprintManager#authenticate usage from
             // FingerprintSettings.
-            return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
+            return BiometricRequestConstants.REASON_AUTH_SETTINGS;
         } else {
-            return BiometricOverlayConstants.REASON_AUTH_OTHER;
+            return BiometricRequestConstants.REASON_AUTH_OTHER;
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java
new file mode 100644
index 0000000..5863535
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.AuthenticationStateListener;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Low-level callback interface between BiometricManager and AuthService. Allows core system
+ * services (e.g. SystemUI) to register and unregister listeners for updates about the current
+ * state of biometric authentication.
+ * @hide */
+public class AuthenticationStateListeners implements IBinder.DeathRecipient {
+
+    private static final String TAG = "AuthenticationStateListeners";
+
+    @NonNull
+    private final CopyOnWriteArrayList<AuthenticationStateListener> mAuthenticationStateListeners =
+            new CopyOnWriteArrayList<>();
+
+    /**
+     * Enables clients to register an AuthenticationStateListener for updates about the current
+     * state of biometric authentication.
+     * @param listener listener to register
+     */
+    public void registerAuthenticationStateListener(
+            @NonNull AuthenticationStateListener listener) {
+        mAuthenticationStateListeners.add(listener);
+        try {
+            listener.asBinder().linkToDeath(this, 0 /* flags */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to link to death", e);
+        }
+    }
+
+    /**
+     * Enables clients to unregister an AuthenticationStateListener.
+     * @param listener listener to register
+     */
+    public void unregisterAuthenticationStateListener(
+            @NonNull AuthenticationStateListener listener) {
+        mAuthenticationStateListeners.remove(listener);
+    }
+
+    /**
+     * Defines behavior in response to authentication starting
+     * @param requestReason reason from [BiometricRequestConstants.RequestReason] for requesting
+     * authentication starting
+     */
+    public void onAuthenticationStarted(int requestReason) {
+        for (AuthenticationStateListener listener: mAuthenticationStateListeners) {
+            try {
+                listener.onAuthenticationStarted(requestReason);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception in notifying listener that authentication "
+                        + "started", e);
+            }
+        }
+    }
+
+    /**
+     * Defines behavior in response to authentication stopping
+     */
+    public void onAuthenticationStopped() {
+        for (AuthenticationStateListener listener: mAuthenticationStateListeners) {
+            try {
+                listener.onAuthenticationStopped();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception in notifying listener that authentication "
+                        + "stopped", e);
+            }
+        }
+    }
+
+    @Override
+    public void binderDied() {
+        // Do nothing, handled below
+    }
+
+    @Override
+    public void binderDied(IBinder who) {
+        Slog.w(TAG, "Callback binder died: " + who);
+        if (mAuthenticationStateListeners.removeIf(listener -> listener.asBinder().equals(who))) {
+            Slog.w(TAG, "Removed dead listener for " + who);
+        } else {
+            Slog.w(TAG, "No dead listeners found");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 483ce75..2c4d30b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -135,14 +135,14 @@
         return true;
     }
 
-    protected int getOverlayReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+    protected int getRequestReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
         switch (reason) {
             case FingerprintManager.ENROLL_FIND_SENSOR:
-                return BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+                return BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR;
             case FingerprintManager.ENROLL_ENROLL:
-                return BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
+                return BiometricRequestConstants.REASON_ENROLL_ENROLLING;
             default:
-                return BiometricOverlayConstants.REASON_UNKNOWN;
+                return BiometricRequestConstants.REASON_UNKNOWN;
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
index aeb6b6e..3d20efc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
+++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
@@ -16,9 +16,11 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
@@ -42,6 +44,8 @@
     private static final String TAG = "SensorOverlays";
 
     @NonNull private final Optional<IUdfpsOverlayController> mUdfpsOverlayController;
+
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @NonNull private final Optional<ISidefpsController> mSidefpsController;
 
     /**
@@ -58,19 +62,32 @@
     }
 
     /**
+     * Create an overlay controller for each modality.
+     *
+     * @param udfpsOverlayController under display fps or null if not present on device
+     */
+    public SensorOverlays(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+        mUdfpsOverlayController = Optional.ofNullable(udfpsOverlayController);
+        // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+        mSidefpsController = Optional.empty();
+    }
+
+    /**
      * Show the overlay.
      *
      * @param sensorId sensor id
      * @param reason reason for showing
      * @param client client performing operation
      */
-    public void show(int sensorId, @BiometricOverlayConstants.ShowReason int reason,
+    public void show(int sensorId, @BiometricRequestConstants.RequestReason int reason,
             @NonNull AcquisitionClient<?> client) {
-        if (mSidefpsController.isPresent()) {
-            try {
-                mSidefpsController.get().show(sensorId, reason);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
+        if (!sidefpsControllerRefactor()) {
+            if (mSidefpsController.isPresent()) {
+                try {
+                    mSidefpsController.get().show(sensorId, reason);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
+                }
             }
         }
 
@@ -98,11 +115,13 @@
      * @param sensorId sensor id
      */
     public void hide(int sensorId) {
-        if (mSidefpsController.isPresent()) {
-            try {
-                mSidefpsController.get().hide(sensorId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
+        if (!sidefpsControllerRefactor()) {
+            if (mSidefpsController.isPresent()) {
+                try {
+                    mSidefpsController.get().hide(sensorId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 7695543..83b306b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -33,6 +33,7 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.biometrics.AuthenticationStateListener;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -81,6 +82,7 @@
 import com.android.server.SystemService;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -128,6 +130,8 @@
     private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
             mBiometricStateCallback;
     @NonNull
+    private final AuthenticationStateListeners mAuthenticationStateListeners;
+    @NonNull
     private final Handler mHandler;
     @NonNull
     private final FingerprintServiceRegistry mRegistry;
@@ -891,6 +895,7 @@
                 return providers;
             });
         }
+
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override
         public void addAuthenticatorsRegisteredCallback(
@@ -902,6 +907,24 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override
+        public void registerAuthenticationStateListener(
+                @NonNull AuthenticationStateListener listener) {
+            super.registerAuthenticationStateListener_enforcePermission();
+
+            mAuthenticationStateListeners.registerAuthenticationStateListener(listener);
+        }
+
+        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+        @Override
+        public void unregisterAuthenticationStateListener(
+                @NonNull AuthenticationStateListener listener) {
+            super.unregisterAuthenticationStateListener_enforcePermission();
+
+            mAuthenticationStateListeners.unregisterAuthenticationStateListener(listener);
+        }
+
+        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+        @Override
         public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
             super.registerBiometricStateListener_enforcePermission();
 
@@ -957,6 +980,7 @@
             }
         }
 
+        // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override
         public void setSidefpsController(@NonNull ISidefpsController controller) {
@@ -1014,6 +1038,7 @@
         mLockoutResetDispatcher = new LockoutResetDispatcher(context);
         mLockPatternUtils = new LockPatternUtils(context);
         mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
+        mAuthenticationStateListeners = new AuthenticationStateListeners();
         mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider :
                 (name) -> {
                     final String fqName = IFingerprint.DESCRIPTOR + "/" + name;
@@ -1022,9 +1047,9 @@
                     if (fp != null) {
                         try {
                             return new FingerprintProvider(getContext(),
-                                    mBiometricStateCallback, fp.getSensorProps(), name,
-                                    mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
-                                    mBiometricContext);
+                                    mBiometricStateCallback, mAuthenticationStateListeners,
+                                    fp.getSensorProps(), name, mLockoutResetDispatcher,
+                                    mGestureAvailabilityDispatcher, mBiometricContext);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
                         }
@@ -1086,13 +1111,13 @@
                     Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
                     UserHandle.USER_CURRENT) != 0) {
                 fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
-                        mBiometricStateCallback, hidlSensor,
-                        mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
+                        mBiometricStateCallback, mAuthenticationStateListeners,
+                        hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
                         BiometricContext.getInstance(getContext()));
             } else {
                 fingerprint21 = Fingerprint21.newInstance(getContext(),
-                        mBiometricStateCallback, hidlSensor, mHandler,
-                        mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+                        mBiometricStateCallback, mAuthenticationStateListeners, hidlSensor,
+                        mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
             }
             providers.add(fingerprint21);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index a15d1a4..fc37d70 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -135,6 +135,7 @@
 
     void onPowerPressed();
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     /**
      * Sets side-fps controller
      * @param controller side-fps controller
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 337c3c2..29c5a3d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskStackListener;
@@ -48,6 +50,7 @@
 import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -84,6 +87,7 @@
     private final int mSkipWaitForPowerVendorAcquireMessage;
     private final long mFingerUpIgnoresPower = 500;
     private final AuthSessionCoordinator mAuthSessionCoordinator;
+    @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
     @Nullable
     private ICancellationSignal mCancellationSignal;
     private boolean mIsPointerDown;
@@ -110,7 +114,9 @@
             boolean isStrongBiometric,
             @Nullable TaskStackListener taskStackListener,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
+            // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
             @Nullable ISidefpsController sidefpsController,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull Handler handler,
@@ -136,7 +142,12 @@
                 false /* shouldVibrate */,
                 biometricStrength);
         setRequestId(requestId);
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        if (sidefpsControllerRefactor()) {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+        } else {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        }
+        mAuthenticationStateListeners = authenticationStateListeners;
         mSensorProps = sensorProps;
         mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
         mHandler = handler;
@@ -216,6 +227,9 @@
         if (authenticated) {
             mState = STATE_STOPPED;
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
         } else {
             mState = STATE_STARTED_PAUSED_ATTEMPTED;
         }
@@ -241,6 +255,9 @@
                 // controlled by the HAL, the framework must stop the sensor before finishing the
                 // client.
                 mSensorOverlays.hide(getSensorId());
+                if (sidefpsControllerRefactor()) {
+                    mAuthenticationStateListeners.onAuthenticationStopped();
+                }
                 onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
                 cancel();
             }
@@ -266,11 +283,17 @@
         }
 
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
     }
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+        mSensorOverlays.show(getSensorId(), getRequestReason(), this);
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
+        }
 
         try {
             mCancellationSignal = doAuthenticate();
@@ -280,6 +303,9 @@
                     BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
             mCallback.onClientFinished(this, false /* success */);
         }
     }
@@ -323,6 +349,9 @@
     @Override
     protected void stopHalOperation() {
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
         unsubscribeBiometricContext();
 
         if (mCancellationSignal != null) {
@@ -423,6 +452,9 @@
         }
 
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
         mCallback.onClientFinished(this, false /* success */);
     }
 
@@ -450,6 +482,9 @@
         }
 
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
         mCallback.onClientFinished(this, false /* success */);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index e2413ee..e58e5ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -16,10 +16,12 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -66,7 +68,12 @@
                 true /* shouldVibrate */, biometricLogger, biometricContext);
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
+        if (sidefpsControllerRefactor()) {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+        } else {
+            mSensorOverlays = new SensorOverlays(
+                    udfpsOverlayController, null /* sideFpsController */);
+        }
         mOptions = options;
     }
 
@@ -93,7 +100,7 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+        mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
                 this);
 
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 06550d8..c0761ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -42,6 +44,7 @@
 import com.android.server.biometrics.log.CallbackWithProbe;
 import com.android.server.biometrics.log.OperationContextExt;
 import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -65,6 +68,7 @@
     @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
 
     private final @FingerprintManager.EnrollReason int mEnrollReason;
+    @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
     @Nullable private ICancellationSignal mCancellationSignal;
     private final int mMaxTemplatesPerUser;
     private boolean mIsPointerDown;
@@ -80,15 +84,18 @@
         }
     }
 
-    public FingerprintEnrollClient(@NonNull Context context,
-            @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, long requestId,
+    public FingerprintEnrollClient(
+            @NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+            @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
+            // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
             @Nullable ISidefpsController sidefpsController,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
         // UDFPS haptics occur when an image is acquired (instead of when the result is known)
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
@@ -96,7 +103,13 @@
                 logger, biometricContext);
         setRequestId(requestId);
         mSensorProps = sensorProps;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        if (sidefpsControllerRefactor()) {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+        } else {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        }
+        mAuthenticationStateListeners = authenticationStateListeners;
+
         mMaxTemplatesPerUser = maxTemplatesPerUser;
 
         mALSProbeCallback = getLogger().getAmbientLightProbe(true /* startWithClient */);
@@ -130,7 +143,11 @@
 
         if (remaining == 0) {
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
         }
+
     }
 
     @Override
@@ -159,8 +176,10 @@
     @Override
     public void onError(int errorCode, int vendorCode) {
         super.onError(errorCode, vendorCode);
-
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
     }
 
     @Override
@@ -171,8 +190,12 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason),
+        mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
                 this);
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStarted(
+                    getRequestReasonFromEnrollReason(mEnrollReason));
+        }
 
         BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
         try {
@@ -210,6 +233,10 @@
     @Override
     protected void stopHalOperation() {
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
+
         unsubscribeBiometricContext();
 
         if (mCancellationSignal != null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 9985b06..032ab87 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -64,6 +64,7 @@
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -108,6 +109,8 @@
     @NonNull
     private final BiometricStateCallback mBiometricStateCallback;
     @NonNull
+    private final AuthenticationStateListeners mAuthenticationStateListeners;
+    @NonNull
     private final String mHalInstanceName;
     @NonNull
     private final Handler mHandler;
@@ -122,6 +125,7 @@
     @NonNull private final BiometricContext mBiometricContext;
     @Nullable private IFingerprint mDaemon;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Nullable private ISidefpsController mSidefpsController;
     private AuthSessionCoordinator mAuthSessionCoordinator;
     @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
@@ -157,16 +161,19 @@
 
     public FingerprintProvider(@NonNull Context context,
             @NonNull BiometricStateCallback biometricStateCallback,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull SensorProps[] props, @NonNull String halInstanceName,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext) {
-        this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
-                gestureAvailabilityDispatcher, biometricContext, null /* daemon */);
+        this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
+                lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
+                null /* daemon */);
     }
 
     @VisibleForTesting FingerprintProvider(@NonNull Context context,
             @NonNull BiometricStateCallback biometricStateCallback,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull SensorProps[] props, @NonNull String halInstanceName,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@@ -174,6 +181,7 @@
             IFingerprint daemon) {
         mContext = context;
         mBiometricStateCallback = biometricStateCallback;
+        mAuthenticationStateListeners = authenticationStateListeners;
         mHalInstanceName = halInstanceName;
         mFingerprintSensors = new SensorList<>(ActivityManager.getService());
         mHandler = new Handler(Looper.getMainLooper());
@@ -434,7 +442,7 @@
                     mBiometricContext,
                     mFingerprintSensors.get(sensorId).getSensorProperties(),
                     mUdfpsOverlayController, mSidefpsController,
-                    maxTemplatesPerUser, enrollReason);
+                    mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason);
             scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
                     mBiometricStateCallback, new ClientMonitorCallback() {
                 @Override
@@ -498,7 +506,7 @@
                     mBiometricContext, isStrongBiometric,
                     mTaskStackListener,
                     mUdfpsOverlayController, mSidefpsController,
-                    allowBackgroundAuthentication,
+                    mAuthenticationStateListeners, allowBackgroundAuthentication,
                     mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
                     Utils.getCurrentStrength(sensorId),
                     SystemClock.elapsedRealtimeClock(),
@@ -732,6 +740,7 @@
         }
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Override
     public void setSidefpsController(@NonNull ISidefpsController controller) {
         mSidefpsController = controller;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 8bfa560..d3cecd0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -68,6 +68,7 @@
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -115,6 +116,7 @@
 
     final Context mContext;
     @NonNull private final BiometricStateCallback mBiometricStateCallback;
+    @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
     private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
     private final BiometricScheduler mScheduler;
@@ -128,6 +130,8 @@
     @Nullable private IBiometricsFingerprint mDaemon;
     @NonNull private final HalResultController mHalResultController;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Nullable private ISidefpsController mSidefpsController;
     @NonNull private final BiometricContext mBiometricContext;
     @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector;
@@ -330,6 +334,7 @@
     @VisibleForTesting
     Fingerprint21(@NonNull Context context,
             @NonNull BiometricStateCallback biometricStateCallback,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull BiometricScheduler scheduler,
             @NonNull Handler handler,
@@ -338,6 +343,7 @@
             @NonNull BiometricContext biometricContext) {
         mContext = context;
         mBiometricStateCallback = biometricStateCallback;
+        mAuthenticationStateListeners = authenticationStateListeners;
         mBiometricContext = biometricContext;
 
         mSensorProperties = sensorProps;
@@ -378,6 +384,7 @@
 
     public static Fingerprint21 newInstance(@NonNull Context context,
             @NonNull BiometricStateCallback biometricStateCallback,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@@ -388,8 +395,9 @@
                         gestureAvailabilityDispatcher);
         final HalResultController controller = new HalResultController(sensorProps.sensorId,
                 context, handler, scheduler);
-        return new Fingerprint21(context, biometricStateCallback, sensorProps, scheduler, handler,
-                lockoutResetDispatcher, controller, BiometricContext.getInstance(context));
+        return new Fingerprint21(context, biometricStateCallback, authenticationStateListeners,
+                sensorProps, scheduler, handler, lockoutResetDispatcher, controller,
+                BiometricContext.getInstance(context));
     }
 
     @Override
@@ -719,7 +727,10 @@
                 mSensorProperties.sensorId,
                 createLogger(BiometricsProtoEnums.ACTION_ENROLL,
                         BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
-                mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
+                mBiometricContext, mUdfpsOverlayController,
+                // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+                mSidefpsController,
+                mAuthenticationStateListeners, enrollReason);
         mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -761,10 +772,14 @@
                                 BiometricsProtoEnums.CLIENT_UNKNOWN,
                                 mAuthenticationStatsCollector),
                         mBiometricContext, null /* sensorProps */,
-                        mUdfpsOverlayController, mSidefpsController,
+                        mUdfpsOverlayController,
+                        // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
+                        mSidefpsController,
+                        mAuthenticationStateListeners,
                         mContext.getResources().getInteger(
                                 com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
                         enrollReason);
+
         mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -880,6 +895,7 @@
                         mBiometricContext, isStrongBiometric,
                         mTaskStackListener,
                         mUdfpsOverlayController, mSidefpsController,
+                        mAuthenticationStateListeners,
                         allowBackgroundAuthentication, mSensorProperties, mHandler,
                         Utils.getCurrentStrength(mSensorId), null /* clock */, mLockoutTracker);
         mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
@@ -898,6 +914,7 @@
                 mBiometricContext, isStrongBiometric,
                 mTaskStackListener, mLockoutTracker,
                 mUdfpsOverlayController, mSidefpsController,
+                mAuthenticationStateListeners,
                 allowBackgroundAuthentication, mSensorProperties,
                 Utils.getCurrentStrength(mSensorId));
         mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
@@ -1123,6 +1140,7 @@
         mUdfpsOverlayController = controller;
     }
 
+    // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
     @Override
     public void setSidefpsController(@NonNull ISidefpsController controller) {
         mSidefpsController = controller;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index c1a9370..88dae6f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -41,6 +41,7 @@
 import com.android.internal.R;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -248,6 +249,7 @@
 
     public static Fingerprint21UdfpsMock newInstance(@NonNull Context context,
             @NonNull BiometricStateCallback biometricStateCallback,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@@ -259,8 +261,9 @@
                 new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
         final MockHalResultController controller =
                 new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
-        return new Fingerprint21UdfpsMock(context, biometricStateCallback, sensorProps, scheduler,
-                handler, lockoutResetDispatcher, controller, biometricContext);
+        return new Fingerprint21UdfpsMock(context, biometricStateCallback,
+                authenticationStateListeners, sensorProps, scheduler, handler,
+                lockoutResetDispatcher, controller, biometricContext);
     }
 
     private static abstract class FakeFingerRunnable implements Runnable {
@@ -388,14 +391,15 @@
 
     private Fingerprint21UdfpsMock(@NonNull Context context,
             @NonNull BiometricStateCallback biometricStateCallback,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull TestableBiometricScheduler scheduler,
             @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull MockHalResultController controller,
             @NonNull BiometricContext biometricContext) {
-        super(context, biometricStateCallback, sensorProps, scheduler, handler,
-                lockoutResetDispatcher, controller, biometricContext);
+        super(context, biometricStateCallback, authenticationStateListeners, sensorProps, scheduler,
+                handler, lockoutResetDispatcher, controller, biometricContext);
         mScheduler = scheduler;
         mScheduler.init(this);
         mHandler = handler;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 9966e91..4c1d4d6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.hidl;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskStackListener;
@@ -40,6 +42,7 @@
 import com.android.server.biometrics.log.CallbackWithProbe;
 import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -68,6 +71,7 @@
     @NonNull private final SensorOverlays mSensorOverlays;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
     @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
+    @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
 
     private boolean mIsPointerDown;
 
@@ -81,7 +85,9 @@
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutFrameworkImpl lockoutTracker,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
+            // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
             @Nullable ISidefpsController sidefpsController,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @Authenticators.Types int sensorStrength) {
@@ -91,7 +97,12 @@
                 false /* shouldVibrate */, sensorStrength);
         setRequestId(requestId);
         mLockoutFrameworkImpl = lockoutTracker;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        if (sidefpsControllerRefactor()) {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+        } else {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        }
+        mAuthenticationStateListeners = authenticationStateListeners;
         mSensorProps = sensorProps;
         mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
     }
@@ -128,6 +139,9 @@
             mState = STATE_STOPPED;
             resetFailedAttempts(getTargetUserId());
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
         } else {
             mState = STATE_STARTED_PAUSED_ATTEMPTED;
             final @LockoutTracker.LockoutMode int lockoutMode =
@@ -141,6 +155,9 @@
                 // controlled by the HAL, the framework must stop the sensor before finishing the
                 // client.
                 mSensorOverlays.hide(getSensorId());
+                if (sidefpsControllerRefactor()) {
+                    mAuthenticationStateListeners.onAuthenticationStopped();
+                }
                 onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
                 cancel();
             }
@@ -156,6 +173,9 @@
         }
 
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
     }
 
     private void resetFailedAttempts(int userId) {
@@ -205,7 +225,10 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+        mSensorOverlays.show(getSensorId(), getRequestReason(), this);
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
+        }
 
         try {
             // GroupId was never used. In fact, groupId is always the same as userId.
@@ -215,6 +238,9 @@
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
             mCallback.onClientFinished(this, false /* success */);
         }
     }
@@ -222,6 +248,9 @@
     @Override
     protected void stopHalOperation() {
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
 
         try {
             getFreshDaemon().cancel();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 0d7f9f2..6e029d2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -16,12 +16,14 @@
 
 package com.android.server.biometrics.sensors.fingerprint.hidl;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.FingerprintAuthenticateOptions;
@@ -71,7 +73,12 @@
                 options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
                 true /* shouldVibrate */, biometricLogger, biometricContext);
         setRequestId(requestId);
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
+        if (sidefpsControllerRefactor()) {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+        } else {
+            mSensorOverlays = new SensorOverlays(
+                    udfpsOverlayController, null /* sideFpsController */);
+        }
         mIsStrongBiometric = isStrongBiometric;
     }
 
@@ -97,7 +104,7 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+        mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
                 this);
 
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 382e7e2..26332ff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.hidl;
 
+import static com.android.systemui.shared.Flags.sidefpsControllerRefactor;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -34,6 +36,7 @@
 
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -58,22 +61,31 @@
 
     @NonNull private final SensorOverlays mSensorOverlays;
     private final @FingerprintManager.EnrollReason int mEnrollReason;
+    @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
     private boolean mIsPointerDown;
 
-    FingerprintEnrollClient(@NonNull Context context,
-            @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
-            long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
+    FingerprintEnrollClient(
+            @NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
+            @NonNull IBinder token, long requestId,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
+            // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
             @Nullable ISidefpsController sidefpsController,
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
             @FingerprintManager.EnrollReason int enrollReason) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
                 biometricContext);
         setRequestId(requestId);
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        if (sidefpsControllerRefactor()) {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController);
+        } else {
+            mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        }
+        mAuthenticationStateListeners = authenticationStateListeners;
 
         mEnrollReason = enrollReason;
         if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
@@ -110,8 +122,12 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason),
+        mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
                 this);
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStarted(
+                    getRequestReasonFromEnrollReason(mEnrollReason));
+        }
 
         BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
         try {
@@ -122,6 +138,9 @@
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
             mCallback.onClientFinished(this, false /* success */);
         }
     }
@@ -129,6 +148,9 @@
     @Override
     protected void stopHalOperation() {
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
 
         try {
             getFreshDaemon().cancel();
@@ -149,6 +171,9 @@
 
         if (remaining == 0) {
             mSensorOverlays.hide(getSensorId());
+            if (sidefpsControllerRefactor()) {
+                mAuthenticationStateListeners.onAuthenticationStopped();
+            }
         }
     }
 
@@ -170,6 +195,9 @@
         super.onError(errorCode, vendorCode);
 
         mSensorOverlays.hide(getSensorId());
+        if (sidefpsControllerRefactor()) {
+            mAuthenticationStateListeners.onAuthenticationStopped();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index dff14b5..6ec6a12 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -327,7 +327,7 @@
     Optional<DeviceState> getOverrideState() {
         synchronized (mLock) {
             if (mActiveOverride.isPresent()) {
-                return getStateLocked(mActiveOverride.get().getRequestedState());
+                return getStateLocked(mActiveOverride.get().getRequestedStateIdentifier());
             }
             return Optional.empty();
         }
@@ -342,7 +342,7 @@
     Optional<DeviceState> getOverrideBaseState() {
         synchronized (mLock) {
             if (mActiveBaseStateOverride.isPresent()) {
-                return getStateLocked(mActiveBaseStateOverride.get().getRequestedState());
+                return getStateLocked(mActiveBaseStateOverride.get().getRequestedStateIdentifier());
             }
             return Optional.empty();
         }
@@ -499,6 +499,7 @@
      * @return {@code true} if the pending state has changed as a result of this call, {@code false}
      * otherwise.
      */
+    @GuardedBy("mLock")
     private boolean updatePendingStateLocked() {
         if (mPendingState.isPresent()) {
             // Have pending state, can not configure a new state until the state is committed.
@@ -507,7 +508,8 @@
 
         final DeviceState stateToConfigure;
         if (mActiveOverride.isPresent()) {
-            stateToConfigure = getStateLocked(mActiveOverride.get().getRequestedState()).get();
+            stateToConfigure = getStateLocked(
+                    mActiveOverride.get().getRequestedStateIdentifier()).get();
         } else if (mBaseState.isPresent()
                 && isSupportedStateLocked(mBaseState.get().getIdentifier())) {
             // Base state could have recently become unsupported after a change in supported states.
@@ -599,7 +601,7 @@
             // requested state is committed.
             OverrideRequest activeRequest = mActiveOverride.orElse(null);
             if (activeRequest != null
-                    && activeRequest.getRequestedState() == newState.getIdentifier()) {
+                    && activeRequest.getRequestedStateIdentifier() == newState.getIdentifier()) {
                 ProcessRecord processRecord = mProcessRecords.get(activeRequest.getPid());
                 if (processRecord != null) {
                     processRecord.notifyRequestActiveAsync(activeRequest.getToken());
@@ -666,21 +668,21 @@
                 case STATUS_ACTIVE:
                     mActiveOverride = Optional.of(request);
                     mDeviceStateNotificationController.showStateActiveNotificationIfNeeded(
-                            request.getRequestedState(), request.getUid());
+                            request.getRequestedStateIdentifier(), request.getUid());
                     break;
                 case STATUS_CANCELED:
                     if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
                         mActiveOverride = Optional.empty();
                         mDeviceStateNotificationController.cancelNotification(
-                                request.getRequestedState());
+                                request.getRequestedStateIdentifier());
                         if ((flags & FLAG_THERMAL_CRITICAL) == FLAG_THERMAL_CRITICAL) {
                             mDeviceStateNotificationController
                                     .showThermalCriticalNotificationIfNeeded(
-                                            request.getRequestedState());
+                                            request.getRequestedStateIdentifier());
                         } else if ((flags & FLAG_POWER_SAVE_ENABLED) == FLAG_POWER_SAVE_ENABLED) {
                             mDeviceStateNotificationController
                                     .showPowerSaveNotificationIfNeeded(
-                                            request.getRequestedState());
+                                            request.getRequestedStateIdentifier());
                         }
                     }
                     break;
@@ -723,7 +725,7 @@
      */
     @GuardedBy("mLock")
     private void enableBaseStateRequestLocked(OverrideRequest request) {
-        setBaseState(request.getRequestedState());
+        setBaseState(request.getRequestedStateIdentifier());
         mActiveBaseStateOverride = Optional.of(request);
         ProcessRecord processRecord = mProcessRecords.get(request.getPid());
         processRecord.notifyRequestActiveAsync(request.getToken());
@@ -762,6 +764,11 @@
         synchronized (mLock) {
             mProcessRecords.remove(processRecord.mPid);
             mOverrideRequestController.handleProcessDied(processRecord.mPid);
+
+            if (shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
+                OverrideRequest request = mActiveOverride.get();
+                mOverrideRequestController.cancelRequest(request);
+            }
         }
     }
 
@@ -787,7 +794,7 @@
             }
 
             OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
-                    state, flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                    deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
             // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
             if (!hasControlDeviceStatePermission && mRearDisplayState != null
@@ -848,7 +855,7 @@
             }
 
             OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
-                    state, flags, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+                    deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_BASE_STATE);
             mOverrideRequestController.addBaseStateRequest(request);
         }
     }
@@ -1318,7 +1325,7 @@
         if (mActiveOverride.isEmpty()) {
             return false;
         }
-        int identifier = mActiveOverride.get().getRequestedState();
+        int identifier = mActiveOverride.get().getRequestedStateIdentifier();
         DeviceState deviceState = mDeviceStates.get(identifier);
         return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
     }
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java
index 74cf184..20485c1 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequest.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java
@@ -17,6 +17,7 @@
 package com.android.server.devicestate;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.hardware.devicestate.DeviceStateRequest;
 import android.os.IBinder;
 
@@ -32,7 +33,8 @@
     private final IBinder mToken;
     private final int mPid;
     private final int mUid;
-    private final int mRequestedState;
+    @NonNull
+    private final DeviceState mRequestedState;
     @DeviceStateRequest.RequestFlags
     private final int mFlags;
     @OverrideRequestType
@@ -69,7 +71,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface OverrideRequestType {}
 
-    OverrideRequest(IBinder token, int pid, int uid, int requestedState,
+    OverrideRequest(IBinder token, int pid, int uid, @NonNull DeviceState requestedState,
             @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) {
         mToken = token;
         mPid = pid;
@@ -91,10 +93,15 @@
         return mUid;
     }
 
-    int getRequestedState() {
+    @NonNull
+    DeviceState getRequestedDeviceState() {
         return mRequestedState;
     }
 
+    int getRequestedStateIdentifier() {
+        return mRequestedState.getIdentifier();
+    }
+
     @DeviceStateRequest.RequestFlags
     int getFlags() {
         return mFlags;
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 46f0bc0..f5f2fa8 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -204,6 +204,12 @@
         }
 
         if (mRequest != null && mRequest.getPid() == pid) {
+            if (mRequest.getRequestedDeviceState().hasFlag(
+                    DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)) {
+                cancelCurrentRequestLocked();
+                return;
+            }
+
             if (mStickyRequestsAllowed) {
                 // Do not cancel the requests now because sticky requests are allowed. These
                 // requests will be cancelled on a call to cancelStickyRequests().
@@ -219,7 +225,7 @@
      * listener of all changes to request status as a result of this change.
      */
     void handleBaseStateChanged(int state) {
-        if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
+        if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedStateIdentifier()) {
             cancelBaseStateOverrideRequest();
         }
         if (mRequest == null) {
@@ -246,11 +252,12 @@
         flags |= isThermalCritical ? FLAG_THERMAL_CRITICAL : 0;
         flags |= isPowerSaveEnabled ? FLAG_POWER_SAVE_ENABLED : 0;
         if (mBaseStateRequest != null && !contains(newSupportedStates,
-                mBaseStateRequest.getRequestedState())) {
+                mBaseStateRequest.getRequestedStateIdentifier())) {
             cancelCurrentBaseStateRequestLocked(flags);
         }
 
-        if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
+        if (mRequest != null && !contains(newSupportedStates,
+                mRequest.getRequestedStateIdentifier())) {
             cancelCurrentRequestLocked(flags);
         }
     }
@@ -262,7 +269,7 @@
         pw.println("Override Request active: " + requestActive);
         if (requestActive) {
             pw.println("Request: mPid=" + overrideRequest.getPid()
-                    + ", mRequestedState=" + overrideRequest.getRequestedState()
+                    + ", mRequestedState=" + overrideRequest.getRequestedStateIdentifier()
                     + ", mFlags=" + overrideRequest.getFlags()
                     + ", mStatus=" + statusToString(STATUS_ACTIVE));
         }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 80c3a27..b542893 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -583,8 +583,8 @@
         pw.println("  mCurrentLightSensorRate=" + mCurrentLightSensorRate);
         pw.println("  mAmbientLux=" + mAmbientLux);
         pw.println("  mAmbientLuxValid=" + mAmbientLuxValid);
-        pw.println("  mPreThesholdLux=" + mPreThresholdLux);
-        pw.println("  mPreThesholdBrightness=" + mPreThresholdBrightness);
+        pw.println("  mPreThresholdLux=" + mPreThresholdLux);
+        pw.println("  mPreThresholdBrightness=" + mPreThresholdBrightness);
         pw.println("  mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
         pw.println("  mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
         pw.println("  mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c440a64..16e043c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -6499,10 +6499,12 @@
                                 nextEnabledImes).getId();
 
                         // Reset enabled IMEs.
-                        settings.putEnabledInputMethodsStr("");
-                        nextEnabledImes.forEach(
-                                imi -> settings.appendAndPutEnabledInputMethodLocked(
-                                        imi.getId()));
+                        final String[] nextEnabledImeIds = new String[nextEnabledImes.size()];
+                        for (int i = 0; i < nextEnabledImeIds.length; ++i) {
+                            nextEnabledImeIds[i] = nextEnabledImes.get(i).getId();
+                        }
+                        settings.putEnabledInputMethodsStr(InputMethodUtils.concatEnabledImeIds(
+                                settings.getEnabledInputMethodsStr(), nextEnabledImeIds));
 
                         // Reset selected IME.
                         settings.putSelectedInputMethod(nextIme);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 2128356..773293f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -33,6 +33,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Printer;
@@ -50,6 +51,8 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.StringJoiner;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -952,24 +955,64 @@
         final String enabledInputMethodsStr = TextUtils.nullIfEmpty(
                 SecureSettingsWrapper.getString(Settings.Secure.ENABLED_INPUT_METHODS, null,
                         userId));
-        if (enabledInputMethodsStr == null) {
-            return List.of();
+        final ArrayList<String> result = new ArrayList<>();
+        splitEnabledImeStr(enabledInputMethodsStr, result::add);
+        return result;
+    }
+
+    /**
+     * Split enabled IME string ({@link Settings.Secure#ENABLED_INPUT_METHODS}) into IME IDs.
+     *
+     * @param text a text formatted with {@link Settings.Secure#ENABLED_INPUT_METHODS}.
+     * @param consumer {@link Consumer} called back when a new IME ID is found.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    static void splitEnabledImeStr(@Nullable String text, @NonNull Consumer<String> consumer) {
+        if (TextUtils.isEmpty(text)) {
+            return;
         }
         final TextUtils.SimpleStringSplitter inputMethodSplitter =
                 new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
         final TextUtils.SimpleStringSplitter subtypeSplitter =
                 new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
-        inputMethodSplitter.setString(enabledInputMethodsStr);
-        final ArrayList<String> result = new ArrayList<>();
+        inputMethodSplitter.setString(text);
         while (inputMethodSplitter.hasNext()) {
             String nextImsStr = inputMethodSplitter.next();
             subtypeSplitter.setString(nextImsStr);
             if (subtypeSplitter.hasNext()) {
                 // The first element is ime id.
-                result.add(subtypeSplitter.next());
+                consumer.accept(subtypeSplitter.next());
             }
         }
-        return result;
+    }
+
+    /**
+     * Concat given IME IDs with an existing enabled IME
+     * ({@link Settings.Secure#ENABLED_INPUT_METHODS}).
+     *
+     * @param existingEnabledImeId an existing {@link Settings.Secure#ENABLED_INPUT_METHODS} to
+     *                             which {@code imeIDs} will be added.
+     * @param imeIds an array of IME IDs to be added. For IME IDs that are already seen in
+     *               {@code existingEnabledImeId} will be skipped.
+     * @return a new enabled IME ID string that can be stored in
+     *         {@link Settings.Secure#ENABLED_INPUT_METHODS}.
+     */
+    @NonNull
+    static String concatEnabledImeIds(@NonNull String existingEnabledImeId,
+            @NonNull String... imeIds) {
+        final ArraySet<String> alreadyEnabledIds = new ArraySet<>();
+        final StringJoiner joiner = new StringJoiner(Character.toString(INPUT_METHOD_SEPARATOR));
+        if (!TextUtils.isEmpty(existingEnabledImeId)) {
+            splitEnabledImeStr(existingEnabledImeId, alreadyEnabledIds::add);
+            joiner.add(existingEnabledImeId);
+        }
+        for (String id : imeIds) {
+            if (!alreadyEnabledIds.contains(id)) {
+                joiner.add(id);
+                alreadyEnabledIds.add(id);
+            }
+        }
+        return joiner.toString();
     }
 
     /**
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index ec858ee2..f2dcba4 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -59,6 +59,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
@@ -67,7 +68,6 @@
 import com.android.server.integrity.model.RuleMetadata;
 import com.android.server.pm.PackageManagerServiceUtils;
 import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index bab3cbe..9c27c22 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.contexthub.HostEndpointInfo;
+import android.hardware.contexthub.MessageDeliveryStatus;
 import android.hardware.contexthub.NanSessionRequest;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -467,6 +468,11 @@
                 // TODO(271471342): Implement
             }
 
+            public void handleMessageDeliveryStatus(char hostEndPointId,
+                    MessageDeliveryStatus messageDeliveryStatus) {
+                // TODO(b/312417087): Implement reliable message support
+            }
+
             public byte[] getUuid() {
                 return UUID;
             }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index df9e741..5e18727 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -964,6 +964,7 @@
         disposeUserIfNeededLocked(userRecord); // since router removed from user
     }
 
+    @GuardedBy("mLock")
     private void setDiscoveryRequestWithRouter2Locked(@NonNull RouterRecord routerRecord,
             @NonNull RouteDiscoveryPreference discoveryRequest) {
         if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) {
@@ -1016,6 +1017,7 @@
                         routeListingPreference));
     }
 
+    @GuardedBy("mLock")
     private void setRouteVolumeWithRouter2Locked(@NonNull IMediaRouter2 router,
             @NonNull MediaRoute2Info route, int volume) {
         final IBinder binder = router.asBinder();
@@ -1035,6 +1037,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId,
             @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession,
             @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
@@ -1117,6 +1120,7 @@
                         sessionHints));
     }
 
+    @GuardedBy("mLock")
     private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
             @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
         final IBinder binder = router.asBinder();
@@ -1138,6 +1142,7 @@
                         DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
     }
 
+    @GuardedBy("mLock")
     private void deselectRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
             @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
         final IBinder binder = router.asBinder();
@@ -1159,6 +1164,7 @@
                         DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
     }
 
+    @GuardedBy("mLock")
     private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
             @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
         final IBinder binder = router.asBinder();
@@ -1191,6 +1197,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router,
             @NonNull String uniqueSessionId, int volume) {
         final IBinder binder = router.asBinder();
@@ -1215,6 +1222,7 @@
                         DUMMY_REQUEST_ID, uniqueSessionId, volume));
     }
 
+    @GuardedBy("mLock")
     private void releaseSessionWithRouter2Locked(@NonNull IMediaRouter2 router,
             @NonNull String uniqueSessionId) {
         final IBinder binder = router.asBinder();
@@ -1240,6 +1248,7 @@
 
     // Start of locked methods that are used by MediaRouter2Manager.
 
+    @GuardedBy("mLock")
     private List<RoutingSessionInfo> getRemoteSessionsLocked(
             @NonNull IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
@@ -1330,6 +1339,7 @@
                         UserHandler::notifyInitialRoutesToManager, userRecord.mHandler, manager));
     }
 
+    @GuardedBy("mLock")
     private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) {
         ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder());
         if (managerRecord == null) {
@@ -1355,6 +1365,7 @@
         disposeUserIfNeededLocked(userRecord); // since manager removed from user
     }
 
+    @GuardedBy("mLock")
     private void startScanLocked(@NonNull IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1368,6 +1379,7 @@
         managerRecord.startScan();
     }
 
+    @GuardedBy("mLock")
     private void stopScanLocked(@NonNull IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1381,6 +1393,7 @@
         managerRecord.stopScan();
     }
 
+    @GuardedBy("mLock")
     private void setRouteVolumeWithManagerLocked(int requestId,
             @NonNull IMediaRouter2Manager manager,
             @NonNull MediaRoute2Info route, int volume) {
@@ -1402,6 +1415,7 @@
                         uniqueRequestId, route, volume));
     }
 
+    @GuardedBy("mLock")
     private void requestCreateSessionWithManagerLocked(int requestId,
             @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession,
             @NonNull MediaRoute2Info route) {
@@ -1455,6 +1469,7 @@
                         uniqueRequestId, routerRecord, managerRecord, oldSession, route));
     }
 
+    @GuardedBy("mLock")
     private void selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager,
             @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
         final IBinder binder = manager.asBinder();
@@ -1479,6 +1494,7 @@
                         uniqueRequestId, routerRecord, uniqueSessionId, route));
     }
 
+    @GuardedBy("mLock")
     private void deselectRouteWithManagerLocked(int requestId,
             @NonNull IMediaRouter2Manager manager,
             @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
@@ -1504,6 +1520,7 @@
                         uniqueRequestId, routerRecord, uniqueSessionId, route));
     }
 
+    @GuardedBy("mLock")
     private void transferToRouteWithManagerLocked(int requestId,
             @NonNull IMediaRouter2Manager manager,
             @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
@@ -1529,6 +1546,7 @@
                         uniqueRequestId, routerRecord, uniqueSessionId, route));
     }
 
+    @GuardedBy("mLock")
     private void setSessionVolumeWithManagerLocked(int requestId,
             @NonNull IMediaRouter2Manager manager,
             @NonNull String uniqueSessionId, int volume) {
@@ -1550,6 +1568,7 @@
                         uniqueRequestId, uniqueSessionId, volume));
     }
 
+    @GuardedBy("mLock")
     private void releaseSessionWithManagerLocked(int requestId,
             @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId) {
         final IBinder binder = manager.asBinder();
@@ -1652,6 +1671,7 @@
 
         // TODO: This assumes that only one router exists in a package.
         //       Do this in Android S or later.
+        @GuardedBy("mLock")
         RouterRecord findRouterRecordLocked(String packageName) {
             for (RouterRecord routerRecord : mRouterRecords) {
                 if (TextUtils.equals(routerRecord.mPackageName, packageName)) {
@@ -1739,6 +1759,7 @@
             return mHasModifyAudioRoutingPermission || mHasBluetoothRoutingPermission.get();
         }
 
+        @GuardedBy("mLock")
         public void maybeUpdateSystemRoutingPermissionLocked() {
             boolean oldSystemRoutingPermissionValue = hasSystemRoutingPermission();
             mHasBluetoothRoutingPermission.set(checkCallerHasBluetoothPermissions(mPid, mUid));
@@ -2063,6 +2084,7 @@
                     this, provider, uniqueRequestId, reason));
         }
 
+        @GuardedBy("mLock")
         @Nullable
         public RouterRecord findRouterWithSessionLocked(@NonNull String uniqueSessionId) {
             return mSessionToRouterMap.get(uniqueSessionId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index b12180b..4b8de4e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -19,6 +19,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
+import android.service.notification.DeviceEffectsApplier;
 
 import java.util.Set;
 
@@ -54,4 +55,22 @@
     void cleanupHistoryFiles();
 
     void removeBitmaps();
+
+    /**
+     * Sets the {@link DeviceEffectsApplier} that will be used to apply the different
+     * {@link android.service.notification.ZenDeviceEffects} that are relevant for the platform
+     * when {@link android.service.notification.ZenModeConfig.ZenRule} instances are activated and
+     * deactivated.
+     *
+     * <p>This method is optional and needs only be called if the platform supports non-standard
+     * effects (i.e. any that are not <em>public APIs</em> in
+     * {@link android.service.notification.ZenDeviceEffects}, or if they must be applied in a
+     * non-standard fashion. If not used, a {@link DefaultDeviceEffectsApplier} will be invoked,
+     * which should be sufficient for most devices.
+     *
+     * <p>If this method is called, it <em>must</em> be during system startup and <em>before</em>
+     * the {@link com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START} boot phase.
+     * Otherwise an {@link IllegalStateException} will be thrown.
+     */
+    void setDeviceEffectsApplier(DeviceEffectsApplier applier);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a62d8b8..e7ae610 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -251,6 +251,7 @@
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.ConversationChannelWrapper;
+import android.service.notification.DeviceEffectsApplier;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.IStatusBarNotificationHolder;
@@ -5406,6 +5407,7 @@
         private void validateAutomaticZenRule(AutomaticZenRule rule) {
             Objects.requireNonNull(rule, "automaticZenRule is null");
             Objects.requireNonNull(rule.getName(), "Name is null");
+            rule.validate();
             if (rule.getOwner() == null
                     && rule.getConfigurationActivity() == null) {
                 throw new NullPointerException(
@@ -5463,6 +5465,7 @@
         public void setAutomaticZenRuleState(String id, Condition condition) {
             Objects.requireNonNull(id, "id is null");
             Objects.requireNonNull(condition, "Condition is null");
+            condition.validate();
 
             enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
 
@@ -6962,6 +6965,18 @@
                 }
             }
         }
+
+        @Override
+        public void setDeviceEffectsApplier(DeviceEffectsApplier applier) {
+            if (!android.app.Flags.modesApi()) {
+                return;
+            }
+            if (mZenModeHelper == null) {
+                throw new IllegalStateException("ZenModeHelper is not yet ready!");
+            }
+            // This can also throw IllegalStateException if called too late.
+            mZenModeHelper.setDeviceEffectsApplier(applier);
+        }
     };
 
     private static boolean isBigPictureWithBitmapOrIcon(Notification n) {
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 7b35589..7f58e75e 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -837,13 +837,11 @@
         }
 
         final String removedPackage = packageRemovedInfo.mRemovedPackage;
-        final int removedAppId = packageRemovedInfo.mRemovedAppId;
-        final int uid = packageRemovedInfo.mUid;
         final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
         final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
 
         Bundle extras = new Bundle(2);
-        extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+        extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid);
         extras.putBoolean(Intent.EXTRA_REPLACING, true);
         sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
                 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
@@ -888,8 +886,6 @@
             boolean removedBySystem,
             boolean isArchived) {
         final String removedPackage = packageRemovedInfo.mRemovedPackage;
-        final int removedAppId = packageRemovedInfo.mRemovedAppId;
-        final int uid = packageRemovedInfo.mUid;
         final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
         final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers;
         final int[] instantUserIds = packageRemovedInfo.mInstantUserIds;
@@ -902,8 +898,7 @@
         final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib;
 
         Bundle extras = new Bundle();
-        final int removedUid = removedAppId >= 0  ? removedAppId : uid;
-        extras.putInt(Intent.EXTRA_UID, removedUid);
+        extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid);
         extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
         extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate);
         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
@@ -940,10 +935,10 @@
                 sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED,
                         removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
                         null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
-                packageSender.notifyPackageRemoved(removedPackage, removedUid);
+                packageSender.notifyPackageRemoved(removedPackage, packageRemovedInfo.mUid);
             }
         }
-        if (removedAppId >= 0) {
+        if (packageRemovedInfo.mIsAppIdRemoved) {
             // If a system app's updates are uninstalled the UID is not actually removed. Some
             // services need to know the package name affected.
             if (isReplace) {
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 27f4e11..482807c 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -518,7 +518,9 @@
     boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId)
             throws PackageManager.NameNotFoundException;
 
-    boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId);
+    /** Check if the package is suspending any package. */
+    boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
+            @UserIdInt int suspendingUserId, int targetUserId);
 
     @NonNull
     ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index abfd571..3cb2420 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -96,6 +96,7 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.content.pm.VersionedPackage;
 import android.os.Binder;
 import android.os.Build;
@@ -3864,19 +3865,15 @@
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
-
-                var usingSharedLibraryPair =
-                        getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
                 SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
                         libInfo.getPackageName(), libInfo.getAllCodePaths(),
                         libInfo.getName(), libInfo.getLongVersion(),
                         libInfo.getType(), declaringPackage,
-                        usingSharedLibraryPair.first,
                         (libInfo.getDependencies() == null
                                 ? null
                                 : new ArrayList<>(libInfo.getDependencies())),
-                        libInfo.isNative());
-
+                        libInfo.isNative(),
+                        getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId));
                 if (result == null) {
                     result = new ArrayList<>();
                 }
@@ -5012,11 +5009,13 @@
 
     @Override
     public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
-            @UserIdInt int userId) {
+            @UserIdInt int suspendingUserId, int targetUserId) {
+        final UserPackage suspender = UserPackage.of(suspendingUserId, suspendingPackage);
         for (final PackageStateInternal packageState : getPackageStates().values()) {
-            final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId);
+            final PackageUserStateInternal state =
+                    packageState.getUserStateOrDefault(targetUserId);
             if (state.getSuspendParams() != null
-                    && state.getSuspendParams().containsKey(suspendingPackage)) {
+                    && state.getSuspendParams().containsKey(suspender)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index dcf921c..aa7f0d3 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -510,7 +510,10 @@
             }
             if (clearPackageStateAndReturn) {
                 mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, flags);
-                outInfo.mRemovedAppId = ps.getAppId();
+                // Legacy behavior to report appId as UID here.
+                // The final broadcasts will contain a per-user UID.
+                outInfo.mUid = ps.getAppId();
+                outInfo.mIsAppIdRemoved = true;
                 mPm.scheduleWritePackageRestrictions(user);
                 return;
             }
@@ -555,6 +558,7 @@
             boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
             @NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
         synchronized (mPm.mLock) {
+            // Since the package is being deleted in all users, report appId as the uid
             outInfo.mUid = ps.getAppId();
             outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
                     mPm.snapshotComputer(), ps, allUserHandles,
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 5c4447e..3b9f9c8 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
@@ -30,7 +31,6 @@
 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
 import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
 import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -46,12 +46,12 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedArrayMap;
 
 import java.io.File;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 9b8ee74..0fa7aa5 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -154,12 +154,16 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.parsing.PackageParserException;
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.security.VerityUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -181,8 +185,6 @@
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedLibraryWrapper;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.security.FileIntegrityService;
 import com.android.server.utils.WatchedArrayMap;
@@ -1165,7 +1167,7 @@
                         parseFlags);
                 archivedPackage = request.getPackageLite().getArchivedPackage();
             }
-        } catch (PackageManagerException e) {
+        } catch (PackageManagerException | PackageParserException e) {
             throw new PrepareFailure("Failed parse during installPackageLI", e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -2910,7 +2912,8 @@
                 info.mInstallerPackageName = request.getInstallerPackageName();
                 info.mRemovedUsers = firstUserIds;
                 info.mBroadcastUsers = firstUserIds;
-                info.mRemovedAppId = request.getAppId();
+                info.mUid = request.getAppId();
+                info.mIsAppIdRemoved = true;
                 info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode();
                 info.mRemovedForAllUsers = true;
 
@@ -3856,7 +3859,7 @@
 
         synchronized (mPm.mLock) {
             platformPackage = mPm.getPlatformPackage();
-            var isSystemApp = AndroidPackageUtils.isSystem(parsedPackage);
+            var isSystemApp = AndroidPackageLegacyUtils.isSystem(parsedPackage);
             final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
                     AndroidPackageUtils.getRealPackageOrNull(parsedPackage, isSystemApp));
             realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName,
@@ -4574,7 +4577,7 @@
 
     private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
             throws PackageManagerException {
-        if (!AndroidPackageUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) {
+        if (!AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) {
             SharedUserSetting sharedUserSetting = null;
             try {
                 synchronized (mPm.mLock) {
@@ -4612,7 +4615,7 @@
         final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
                 && ScanPackageUtils.getVendorPartitionVersion() < 28;
         if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
-                && !AndroidPackageUtils.isPrivileged(pkg)
+                && !AndroidPackageLegacyUtils.isPrivileged(pkg)
                 && (pkg.getSharedUserId() != null)
                 && !skipVendorPrivilegeScan) {
             SharedUserSetting sharedUserSetting = null;
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 5494bd9..ee780d9 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -51,12 +51,12 @@
 import android.util.Slog;
 
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.art.model.DexoptResult;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -818,7 +818,8 @@
 
     public void setRemovedAppId(int appId) {
         if (mRemovedInfo != null) {
-            mRemovedInfo.mRemovedAppId = appId;
+            mRemovedInfo.mUid = appId;
+            mRemovedInfo.mIsAppIdRemoved = true;
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java
index 154709a..83831ca 100644
--- a/services/core/java/com/android/server/pm/KnownPackages.java
+++ b/services/core/java/com/android/server/pm/KnownPackages.java
@@ -77,6 +77,8 @@
     // Please note the numbers should be continuous.
     public static final int LAST_KNOWN_PACKAGE = PACKAGE_WEARABLE_SENSING;
 
+    static final String SYSTEM_PACKAGE_NAME = "android";
+
     private final DefaultAppProvider mDefaultAppProvider;
     private final String mRequiredInstallerPackage;
     private final String mRequiredUninstallerPackage;
@@ -186,7 +188,7 @@
             case PACKAGE_SETUP_WIZARD:
                 return snapshot.filterOnlySystemPackages(mSetupWizardPackage);
             case PACKAGE_SYSTEM:
-                return new String[]{"android"};
+                return new String[]{SYSTEM_PACKAGE_NAME};
             case PACKAGE_VERIFIER:
                 return snapshot.filterOnlySystemPackages(mRequiredVerifierPackages);
             case PACKAGE_SYSTEM_TEXT_CLASSIFIER:
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index ba66377..127bf49 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -822,7 +822,7 @@
             return new LauncherActivityInfoInternal(
                     activityInfo,
                     new IncrementalStatesInfo(
-                            false /* isLoading */, 1 /* progress */, 0 /* loadingCompletedTime */),
+                            false /* isLoading */, 0 /* progress */, 0 /* loadingCompletedTime */),
                     user);
         }
 
@@ -1599,6 +1599,33 @@
         }
 
         @Override
+        public @Nullable IntentSender getAppMarketActivityIntent(@NonNull String callingPackage,
+                @Nullable String packageName, @NonNull UserHandle user) {
+            // Only system launchers, which have access to recents should have access to this API.
+            // TODO(b/303803157): Update access control for this API to default Launcher app.
+            if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
+                throw new SecurityException("Caller is not the recents app");
+            }
+            if (!canAccessProfile(user.getIdentifier(),
+                    "Can't access AppMarketActivity for another user")) {
+                return null;
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                // TODO(b/316118005): Add code to launch the app installer for the packageName.
+                Intent appMarketIntent = new Intent(Intent.ACTION_MAIN);
+                appMarketIntent.addCategory(Intent.CATEGORY_APP_MARKET);
+                final PendingIntent pi = PendingIntent.getActivityAsUser(
+                        mContext, /* requestCode */ 0, appMarketIntent, PendingIntent.FLAG_ONE_SHOT
+                                | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
+                        /* options */ null, user);
+                return pi == null ? null : pi.getIntentSender();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
                 String callingFeatureId, ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 6b05edf..2864a8b 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -165,7 +165,7 @@
         Computer snapshot = mPm.snapshotComputer();
         int userId = userHandle.getIdentifier();
         int binderUid = Binder.getCallingUid();
-        if (!PackageManagerServiceUtils.isRootOrShell(binderUid)) {
+        if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) {
             verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid);
         }
         snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 7d6dd62..2942bbb 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -821,6 +821,18 @@
             params.installFlags &= ~PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
         }
 
+        params.installFlags &= ~PackageManager.INSTALL_UNARCHIVE;
+        if (Flags.archiving() && params.appPackageName != null) {
+            PackageStateInternal ps = mPm.snapshotComputer().getPackageStateInternal(
+                    params.appPackageName, SYSTEM_UID);
+            if (ps != null
+                    && PackageArchiver.isArchived(ps.getUserStateOrDefault(userId))
+                    && PackageArchiver.getResponsibleInstallerPackage(ps)
+                            .equals(requestedInstallerPackageName)) {
+                params.installFlags |= PackageManager.INSTALL_UNARCHIVE;
+            }
+        }
+
         if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
                 && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
                 && (snapshot.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 47d1df5..4adb60c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -168,6 +168,7 @@
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.security.VerityUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -181,7 +182,6 @@
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index c737b45..8da1683 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -19,6 +19,8 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
 
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -37,6 +39,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -250,7 +253,7 @@
         getSuspendPackageHelper().removeSuspensionsBySuspendingPackage(snapshot(),
                 new String[]{packageName},
                 (suspendingPackage) -> !PackageManagerService.PLATFORM_PACKAGE_NAME.equals(
-                        suspendingPackage),
+                        suspendingPackage.packageName),
                 userId);
     }
 
@@ -269,7 +272,7 @@
 
     @Override
     @Deprecated
-    public final String getSuspendingPackage(String suspendedPackage, int userId) {
+    public final UserPackage getSuspendingPackage(String suspendedPackage, int userId) {
         return getSuspendPackageHelper().getSuspendingPackage(snapshot(), suspendedPackage, userId,
                 Binder.getCallingUid());
     }
@@ -277,7 +280,7 @@
     @Override
     @Deprecated
     public final SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
-            String suspendingPackage, int userId) {
+            UserPackage suspendingPackage, int userId) {
         return getSuspendPackageHelper().getSuspendedDialogInfo(snapshot(), suspendedPackage,
                 suspendingPackage, userId, Binder.getCallingUid());
     }
@@ -683,14 +686,16 @@
 
     @Override
     @Deprecated
-    public final void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
-        mService.unsuspendForSuspendingPackage(snapshot(), packageName, affectedUser);
+    public final void unsuspendAdminSuspendedPackages(int affectedUser) {
+        final int suspendingUserId = affectedUser;
+        mService.unsuspendForSuspendingPackage(snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId);
     }
 
     @Override
     @Deprecated
-    public final boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
-        return snapshot().isSuspendingAnyPackages(suspendingPackage, userId);
+    public final boolean isAdminSuspendingAnyPackages(int userId) {
+        final int suspendingUserId = userId;
+        return snapshot().isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, suspendingUserId, userId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d369658..bc441b8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -185,6 +185,7 @@
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -238,7 +239,6 @@
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
 import com.android.server.pm.pkg.mutate.PackageStateWrite;
 import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.resolution.ComponentResolver;
 import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
@@ -292,6 +292,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -2013,6 +2014,16 @@
             public boolean hasFeature(String feature) {
                 return PackageManagerService.this.hasSystemFeature(feature, 0);
             }
+
+            @Override
+            public Set<String> getHiddenApiWhitelistedApps() {
+                return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
+            }
+
+            @Override
+            public Set<String> getInstallConstraintsAllowlist() {
+                return SystemConfig.getInstance().getInstallConstraintsAllowlist();
+            }
         };
 
         // CHECKSTYLE:ON IndentationCheck
@@ -3127,7 +3138,7 @@
     }
 
     private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot,
-            boolean quarantined, String callingPackage, int callingUid, int userId,
+            boolean quarantined, UserPackage suspender, int callingUid, int targetUserId,
             String callingMethod) {
         if (callingUid == Process.ROOT_UID
                 // Need to compare app-id to allow system dialogs access on secondary users
@@ -3135,9 +3146,10 @@
             return;
         }
 
-        final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
+        final String ownerPackage =
+                mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
         if (ownerPackage != null) {
-            final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, userId);
+            final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, targetUserId);
             if (ownerUid == callingUid) {
                 return;
             }
@@ -3158,25 +3170,27 @@
                     callingMethod);
         }
 
-        final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId);
+        final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId);
         final boolean allowedPackageUid = packageUid == callingUid;
         // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL
         final boolean allowedShell = callingUid == SHELL_UID
                 && UserHandle.isSameApp(packageUid, callingUid);
 
         if (!allowedShell && !allowedPackageUid) {
-            throw new SecurityException("Calling package " + callingPackage + " in user "
-                    + userId + " does not belong to calling uid " + callingUid);
+            throw new SecurityException("Suspending package " + suspender.packageName
+                    + " in user " + targetUserId + " does not belong to calling uid " + callingUid);
         }
     }
 
     void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
-            @UserIdInt int userId) {
+            @UserIdInt int suspendingUserId) {
         // TODO: This can be replaced by a special parameter to iterate all packages, rather than
         //  this weird pre-collect of all packages.
         final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]);
+        final Predicate<UserPackage> suspenderPredicate =
+                UserPackage.of(suspendingUserId, suspendingPackage)::equals;
         mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer,
-                allPackages, suspendingPackage::equals, userId);
+                allPackages, suspenderPredicate, suspendingUserId);
     }
 
     void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) {
@@ -5249,8 +5263,9 @@
                 if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
                     return null;
                 }
-                return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId,
-                        callingUid);
+                final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage(
+                        snapshot, packageName, userId, callingUid);
+                return suspender != null ? suspender.packageName : null;
             } catch (PackageManager.NameNotFoundException e) {
                 return null;
             }
@@ -6188,7 +6203,8 @@
         @Override
         public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
                 PersistableBundle appExtras, PersistableBundle launcherExtras,
-                SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) {
+                SuspendDialogInfo dialogInfo, int flags, String suspendingPackage,
+                int suspendingUserId, int targetUserId) {
             final int callingUid = Binder.getCallingUid();
             boolean quarantined = false;
             if (Flags.quarantinedEnabled()) {
@@ -6197,14 +6213,15 @@
                 } else if (FeatureFlagUtils.isEnabled(mContext,
                         SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) {
                     final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing);
-                    quarantined = callingPackage.equals(wellbeingPkg);
+                    quarantined = suspendingPackage.equals(wellbeingPkg);
                 }
             }
             final Computer snapshot = snapshotComputer();
-            enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid,
-                    userId, "setPackagesSuspendedAsUser");
+            final UserPackage suspender = UserPackage.of(targetUserId, suspendingPackage);
+            enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid,
+                    targetUserId, "setPackagesSuspendedAsUser");
             return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
-                    appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
+                    appExtras, launcherExtras, dialogInfo, suspender, targetUserId, callingUid,
                     quarantined);
         }
 
@@ -6643,7 +6660,7 @@
             final Computer computer = snapshotComputer();
             final String[] allPackages = computer.getAllAvailablePackageNames();
             mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages,
-                    (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
+                    (suspender) -> !PLATFORM_PACKAGE_NAME.equals(suspender.packageName),
                     userId);
         }
 
@@ -6657,8 +6674,13 @@
         @Override
         public String[] setPackagesSuspendedByAdmin(
                 @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) {
-            return mSuspendPackageHelper.setPackagesSuspendedByAdmin(
-                    snapshotComputer(), userId, packageNames, suspended);
+            final int suspendingUserId = userId;
+            final UserPackage suspender = UserPackage.of(
+                    suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME);
+            return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames,
+                    suspended, null /* appExtras */, null /* launcherExtras */,
+                    null /* dialogInfo */, suspender, userId, Process.SYSTEM_UID,
+                    false /* quarantined */);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 215e952..243fb16 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2827,7 +2827,7 @@
             mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}),
                     suspendedState, ((appExtras.size() > 0) ? appExtras : null),
                     ((launcherExtras.size() > 0) ? launcherExtras : null),
-                    info, flags, callingPackage, translatedUserId);
+                    info, flags, callingPackage, UserHandle.USER_SYSTEM, translatedUserId);
             for (int i = 0; i < packageNames.size(); i++) {
                 final String packageName = packageNames.get(i);
                 pw.println("Package " + packageName + " new suspended state: "
@@ -2872,16 +2872,16 @@
                 UserHandle.USER_NULL, "runGrantRevokePermission"));
 
         List<PackageInfo> packageInfos;
+        PackageManager pm = mContext.createContextAsUser(translatedUser, 0).getPackageManager();
         if (pkg == null) {
-            packageInfos = mContext.getPackageManager().getInstalledPackages(
-                    PackageManager.GET_PERMISSIONS);
+            packageInfos = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
         } else {
             try {
-                packageInfos = Collections.singletonList(
-                        mContext.getPackageManager().getPackageInfo(pkg,
-                                PackageManager.GET_PERMISSIONS));
+                packageInfos = Collections.singletonList(pm.getPackageInfo(pkg,
+                        PackageManager.GET_PERMISSIONS));
             } catch (NameNotFoundException e) {
                 getErrPrintWriter().println("Error: package not found");
+                getOutPrintWriter().println("Failure [package not found]");
                 return 1;
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 7ee1772..881b0b3 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -25,7 +25,7 @@
     String mRemovedPackage;
     String mInstallerPackageName;
     int mUid = -1;
-    int mRemovedAppId = -1;
+    boolean mIsAppIdRemoved = false;
     int[] mOrigUsers;
     int[] mRemovedUsers = null;
     int[] mBroadcastUsers = null;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 7d0a1f6..28a90f3 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -32,6 +32,7 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.content.pm.overlay.OverlayPaths;
 import android.os.UserHandle;
 import android.os.incremental.IncrementalManager;
@@ -952,7 +953,7 @@
 
     void setUserState(int userId, long ceDataInode, long deDataInode, int enabled,
                       boolean installed, boolean stopped, boolean notLaunched, boolean hidden,
-                      int distractionFlags, ArrayMap<String, SuspendParams> suspendParams,
+                      int distractionFlags, ArrayMap<UserPackage, SuspendParams> suspendParams,
                       boolean instantApp, boolean virtualPreload, String lastDisableAppCaller,
                       ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
                       int installReason, int uninstallReason,
@@ -1182,7 +1183,7 @@
             if (state.isSuspended()) {
                 for (int j = 0; j < state.getSuspendParams().size(); j++) {
                     proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE,
-                            state.getSuspendParams().keyAt(j));
+                            state.getSuspendParams().keyAt(j).packageName);
                 }
             }
             proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.isStopped());
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index bb0017c..b6de0e5 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -20,19 +20,23 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
 
+import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX;
 import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
 import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerService.TAG;
 
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
+import android.os.Build;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedLongSparseArray;
 
 import java.util.ArrayList;
@@ -49,6 +53,8 @@
  * as install) led to the request.
  */
 final class ReconcilePackageUtils {
+    private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true;
+
     public static List<ReconciledPackage> reconcilePackages(
             List<InstallRequest> installRequests,
             Map<String, AndroidPackage> allPackages,
@@ -90,6 +96,8 @@
             }
         }
 
+        final AndroidPackage systemPackage = allPackages.get(KnownPackages.SYSTEM_PACKAGE_NAME);
+
         for (InstallRequest installRequest : installRequests) {
             final String installPackageName = installRequest.getParsedPackage().getPackageName();
             final List<SharedLibraryInfo> allowedSharedLibInfos =
@@ -133,6 +141,9 @@
             if (parsedPackage != null) {
                 signingDetails = parsedPackage.getSigningDetails();
             }
+            final boolean isSystemPackage =
+                    ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0);
+            final boolean isApex = (scanFlags & SCAN_AS_APEX) != 0;
             SharedUserSetting sharedUserSetting = settings.getSharedUserSettingLPr(
                     signatureCheckPs);
             if (ksms.shouldCheckUpgradeKeySetLocked(
@@ -141,7 +152,7 @@
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
                 } else {
-                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if (!isSystemPackage) {
                         throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                 "Package " + parsedPackage.getPackageName()
                                         + " upgrade keys do not match the previously installed"
@@ -168,9 +179,23 @@
                         removeAppKeySetData = true;
                     }
 
+                    if (!isSystemPackage && !isApex && signingDetails != null
+                            && systemPackage != null && systemPackage.getSigningDetails() != null
+                            && systemPackage.getSigningDetails().checkCapability(
+                                    signingDetails,
+                                    SigningDetails.CertCapabilities.PERMISSION)) {
+                        Slog.d(TAG, "Non-preload app associated with system signature: "
+                                + signatureCheckPs.getPackageName());
+                        if (!ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE) {
+                            throw new ReconcileFailure(
+                                    INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                    "Non-preload app associated with system signature: "
+                                            + signatureCheckPs.getPackageName());
+                        }
+                    }
+
                     // if this is is a sharedUser, check to see if the new package is signed by a
-                    // newer
-                    // signing certificate than the existing one, and if so, copy over the new
+                    // newer signing certificate than the existing one, and if so, copy over the new
                     // details
                     if (sharedUserSetting != null) {
                         // Attempt to merge the existing lineage for the shared SigningDetails with
@@ -203,7 +228,7 @@
                         }
                     }
                 } catch (PackageManagerException e) {
-                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if (!isSystemPackage) {
                         throw new ReconcileFailure(e);
                     }
                     signingDetails = parsedPackage.getSigningDetails();
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 109d7ba..fefab3b 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -44,12 +44,12 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.parsing.PackageCacher;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -167,7 +167,7 @@
             if (removedPackage != null) {
                 // TODO: Use PackageState for isSystem
                 cleanPackageDataStructuresLILPw(removedPackage,
-                        AndroidPackageUtils.isSystem(removedPackage), chatty);
+                        AndroidPackageLegacyUtils.isSystem(removedPackage), chatty);
             }
         }
     }
@@ -347,8 +347,7 @@
                 mPm.mAppsFilter.removePackage(snapshot,
                         snapshot.getPackageStateInternal(packageName));
                 removedAppId = mPm.mSettings.removePackageLPw(packageName);
-                outInfo.mRemovedAppId = removedAppId;
-
+                outInfo.mIsAppIdRemoved = true;
                 if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
                     // If we don't have a disabled system package to reinstall, the package is
                     // really gone and its permission state should be removed.
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 31a63e0..5c6d61e 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -74,11 +74,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.component.ParsedProcess;
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.PackageInfoUtils;
@@ -86,8 +88,6 @@
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateUtils;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedArraySet;
 
 import dalvik.system.VMRuntime;
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
index 37cf30b..41e2a3f 100644
--- a/services/core/java/com/android/server/pm/ScanRequest.java
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -22,8 +22,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 /** A package to be scanned */
 @VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 75d88da..d6952262 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -51,6 +51,7 @@
 import android.content.pm.Signature;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.overlay.OverlayPaths;
 import android.net.Uri;
@@ -89,6 +90,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedPermission;
@@ -106,7 +108,6 @@
 import com.android.server.backup.PreferredActivityBackupHelper;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
 import com.android.server.pm.permission.LegacyPermissionSettings;
 import com.android.server.pm.permission.LegacyPermissionState;
@@ -1956,7 +1957,7 @@
                         ArchiveState archiveState = null;
 
                         int packageDepth = parser.getDepth();
-                        ArrayMap<String, SuspendParams> suspendParamsMap = null;
+                        ArrayMap<UserPackage, SuspendParams> suspendParamsMap = null;
                         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                                 && (type != XmlPullParser.END_TAG
                                 || parser.getDepth() > packageDepth)) {
@@ -1983,18 +1984,15 @@
                                             parser);
                                     break;
                                 case TAG_SUSPEND_PARAMS:
-                                    final String suspendingPackage = parser.getAttributeValue(null,
-                                            ATTR_SUSPENDING_PACKAGE);
-                                    if (suspendingPackage == null) {
-                                        Slog.wtf(TAG, "No suspendingPackage found inside tag "
-                                                + TAG_SUSPEND_PARAMS);
+                                    Map.Entry<UserPackage, SuspendParams> entry =
+                                            readSuspensionParamsLPr(userId, parser);
+                                    if (entry == null) {
                                         continue;
                                     }
                                     if (suspendParamsMap == null) {
                                         suspendParamsMap = new ArrayMap<>();
                                     }
-                                    suspendParamsMap.put(suspendingPackage,
-                                            SuspendParams.restoreFromXml(parser));
+                                    suspendParamsMap.put(entry.getKey(), entry.getValue());
                                     break;
                                 case TAG_ARCHIVE_STATE:
                                     archiveState = parseArchiveState(parser);
@@ -2016,7 +2014,8 @@
                                     oldSuspendedLauncherExtras,
                                     false /* quarantined */);
                             suspendParamsMap = new ArrayMap<>();
-                            suspendParamsMap.put(oldSuspendingPackage, suspendParams);
+                            suspendParamsMap.put(
+                                    UserPackage.of(userId, oldSuspendingPackage), suspendParams);
                         }
 
                         if (blockUninstall) {
@@ -2058,6 +2057,20 @@
         }
     }
 
+    @Nullable
+    private static Map.Entry<UserPackage, SuspendParams> readSuspensionParamsLPr(
+            int userId, TypedXmlPullParser parser) throws IOException {
+        final String suspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE);
+        if (suspendingPackage == null) {
+            Slog.wtf(TAG, "No suspendingPackage found inside tag " + TAG_SUSPEND_PARAMS);
+            return null;
+        }
+        final int suspendingUserId = userId;
+        return Map.entry(
+                UserPackage.of(suspendingUserId, suspendingPackage),
+                SuspendParams.restoreFromXml(parser));
+    }
+
     private static ArchiveState parseArchiveState(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException {
         String installerTitle = parser.getAttributeValue(null,
@@ -2414,10 +2427,11 @@
                         }
                         if (ustate.isSuspended()) {
                             for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
-                                final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
+                                final UserPackage suspendingPackage =
+                                        ustate.getSuspendParams().keyAt(i);
                                 serializer.startTag(null, TAG_SUSPEND_PARAMS);
                                 serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
-                                        suspendingPackage);
+                                        suspendingPackage.packageName);
                                 final SuspendParams params =
                                         ustate.getSuspendParams().valueAt(i);
                                 if (params != null) {
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index dddc6b0..5c0a15a 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -25,14 +25,14 @@
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
 import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProcessImpl;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedProcessImpl;
 import com.android.server.utils.SnapshotCache;
 import com.android.server.utils.Watchable;
 import com.android.server.utils.WatchedArraySet;
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index b607502..7d87d1b 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -47,11 +47,11 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import java.io.File;
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index c2a960a..4e70cc5 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -27,10 +27,10 @@
 import android.app.AppOpsManager;
 import android.content.Intent;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
@@ -88,8 +88,8 @@
      * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
      *                   should be shown to the user when they try to launch a suspended app.
      *                   Ignored if {@code suspended} is false.
-     * @param callingPackage The caller's package name.
-     * @param userId The user where packages reside.
+     * @param suspendingPackage The caller's package name.
+     * @param targetUserId The user where packages reside.
      * @param callingUid The caller's uid.
      * @return The names of failed packages.
      */
@@ -97,14 +97,14 @@
     String[] setPackagesSuspended(@NonNull Computer snapshot, @Nullable String[] packageNames,
             boolean suspended, @Nullable PersistableBundle appExtras,
             @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
-            @NonNull String callingPackage, @UserIdInt int userId, int callingUid,
+            @NonNull UserPackage suspendingPackage, @UserIdInt int targetUserId, int callingUid,
             boolean quarantined) {
         if (ArrayUtils.isEmpty(packageNames)) {
             return packageNames;
         }
-        if (suspended && !quarantined && !isSuspendAllowedForUser(snapshot, userId,
-                callingUid)) {
-            Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+        if (suspended && !quarantined
+                && !isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
+            Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
             return packageNames;
         }
 
@@ -119,19 +119,21 @@
         final IntArray changedUids = new IntArray(packageNames.length);
 
         final boolean[] canSuspend = suspended
-                ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid)
+                ? canSuspendPackageForUser(snapshot, packageNames, targetUserId, callingUid)
                 : null;
         for (int i = 0; i < packageNames.length; i++) {
             final String packageName = packageNames[i];
-            if (callingPackage.equals(packageName)) {
-                Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+            if (suspendingPackage.packageName.equals(packageName)
+                    && suspendingPackage.userId == targetUserId) {
+                Slog.w(TAG, "Suspending package: " + suspendingPackage + " trying to "
                         + (suspended ? "" : "un") + "suspend itself. Ignoring");
                 unmodifiablePackages.add(packageName);
                 continue;
             }
             final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
-            if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled()
-                    || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
+            if (packageState == null
+                    || !packageState.getUserStateOrDefault(targetUserId).isInstalled()
+                    || snapshot.shouldFilterApplication(packageState, callingUid, targetUserId)) {
                 Slog.w(TAG, "Could not find package setting for package: " + packageName
                         + ". Skipping suspending/un-suspending.");
                 unmodifiablePackages.add(packageName);
@@ -142,34 +144,34 @@
                 continue;
             }
 
-            final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
-                    packageState.getUserStateOrDefault(userId).getSuspendParams();
+            final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
+                    packageState.getUserStateOrDefault(targetUserId).getSuspendParams();
             final SuspendParams oldSuspendParams = suspendParamsMap == null
-                    ? null : suspendParamsMap.get(callingPackage);
+                    ? null : suspendParamsMap.get(suspendingPackage);
             boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams);
 
             if (suspended && !changed) {
                 // Carried over API behavior, must notify change even if no change
                 notifyPackagesList.add(packageName);
                 notifyUids.add(
-                        UserHandle.getUid(userId, packageState.getAppId()));
+                        UserHandle.getUid(targetUserId, packageState.getAppId()));
                 continue;
             }
 
-            // If only the callingPackage is suspending this package,
+            // If only the suspendingPackage is suspending this package,
             // it will be unsuspended when this change is committed
             boolean packageUnsuspended = !suspended
                     && CollectionUtils.size(suspendParamsMap) == 1
-                    && suspendParamsMap.containsKey(callingPackage);
+                    && suspendParamsMap.containsKey(suspendingPackage);
             if (suspended || packageUnsuspended) {
                 // Always notify of a suspend call + notify when fully unsuspended
                 notifyPackagesList.add(packageName);
-                notifyUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                notifyUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
             }
 
             if (changed) {
                 changedPackagesList.add(packageName);
-                changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                changedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
             } else {
                 Slog.w(TAG, "No change is needed for package: " + packageName
                         + ". Skipping suspending/un-suspending.");
@@ -181,11 +183,11 @@
             for (int index = 0; index < size; index++) {
                 final String packageName  = changedPackagesList.valueAt(index);
                 final PackageUserStateWrite userState = mutator.forPackage(packageName)
-                        .userState(userId);
+                        .userState(targetUserId);
                 if (suspended) {
-                    userState.putSuspendParams(callingPackage, newSuspendParams);
+                    userState.putSuspendParams(suspendingPackage, newSuspendParams);
                 } else {
-                    userState.removeSuspension(callingPackage);
+                    userState.removeSuspension(suspendingPackage);
                 }
             }
         });
@@ -197,17 +199,17 @@
             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
                     suspended ? Intent.ACTION_PACKAGES_SUSPENDED
                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    changedPackages, notifyUids.toArray(), quarantined, userId);
+                    changedPackages, notifyUids.toArray(), quarantined, targetUserId);
             mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
-                    suspended, userId);
-            mPm.scheduleWritePackageRestrictions(userId);
+                    suspended, targetUserId);
+            mPm.scheduleWritePackageRestrictions(targetUserId);
         }
         // Send the suspension changed broadcast to ensure suspension state is not stale.
         if (!changedPackagesList.isEmpty()) {
             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
                     Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
                     changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
-                    userId);
+                    targetUserId);
         }
         return unmodifiablePackages.toArray(new String[0]);
     }
@@ -216,19 +218,19 @@
      * Returns the names in the {@code packageNames} which can not be suspended by the caller.
      *
      * @param packageNames The names of packages to check.
-     * @param userId The user where packages reside.
+     * @param targetUserId The user where packages reside.
      * @param callingUid The caller's uid.
      * @return The names of packages which are Unsuspendable.
      */
     @NonNull
     String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot,
-            @NonNull String[] packageNames, @UserIdInt int userId, int callingUid) {
-        if (!isSuspendAllowedForUser(snapshot, userId, callingUid)) {
-            Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+            @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid) {
+        if (!isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
+            Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
             return packageNames;
         }
         final ArraySet<String> unactionablePackages = new ArraySet<>();
-        final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, userId,
+        final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, targetUserId,
                 callingUid);
         for (int i = 0; i < packageNames.length; i++) {
             if (!canSuspend[i]) {
@@ -237,7 +239,7 @@
             }
             final PackageStateInternal packageState =
                     snapshot.getPackageStateForInstalledAndFiltered(
-                            packageNames[i], callingUid, userId);
+                            packageNames[i], callingUid, targetUserId);
             if (packageState == null) {
                 Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
                 unactionablePackages.add(packageNames[i]);
@@ -285,30 +287,31 @@
      * @param packagesToChange The packages on which the suspension are to be removed.
      * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
      *                                   suspensions will be removed.
-     * @param userId The user for which the changes are taking place.
+     * @param targetUserId The user for which the changes are taking place.
      */
     void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
             @NonNull String[] packagesToChange,
-            @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
+            @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId) {
         final List<String> unsuspendedPackages = new ArrayList<>();
         final IntArray unsuspendedUids = new IntArray();
-        final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
+        final ArrayMap<String, ArraySet<UserPackage>> pkgToSuspendingPkgsToCommit =
+                new ArrayMap<>();
         for (String packageName : packagesToChange) {
             final PackageStateInternal packageState =
                     snapshot.getPackageStateInternal(packageName);
             final PackageUserStateInternal packageUserState = packageState == null
-                    ? null : packageState.getUserStateOrDefault(userId);
+                    ? null : packageState.getUserStateOrDefault(targetUserId);
             if (packageUserState == null || !packageUserState.isSuspended()) {
                 continue;
             }
 
-            WatchedArrayMap<String, SuspendParams> suspendParamsMap =
+            WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
                     packageUserState.getSuspendParams();
             int countRemoved = 0;
             for (int index = 0; index < suspendParamsMap.size(); index++) {
-                String suspendingPackage = suspendParamsMap.keyAt(index);
+                UserPackage suspendingPackage = suspendParamsMap.keyAt(index);
                 if (suspendingPackagePredicate.test(suspendingPackage)) {
-                    ArraySet<String> suspendingPkgsToCommit =
+                    ArraySet<UserPackage> suspendingPkgsToCommit =
                             pkgToSuspendingPkgsToCommit.get(packageName);
                     if (suspendingPkgsToCommit == null) {
                         suspendingPkgsToCommit = new ArraySet<>();
@@ -322,31 +325,33 @@
             // Everything would be removed and package unsuspended
             if (countRemoved == suspendParamsMap.size()) {
                 unsuspendedPackages.add(packageState.getPackageName());
-                unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                unsuspendedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
             }
         }
 
         mPm.commitPackageStateMutation(null, mutator -> {
             for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
                 String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
-                ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
-                PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId);
+                ArraySet<UserPackage> packagesToRemove =
+                        pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
+                PackageUserStateWrite userState =
+                        mutator.forPackage(packageName).userState(targetUserId);
                 for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
                     userState.removeSuspension(packagesToRemove.valueAt(setIndex));
                 }
             }
         });
 
-        mPm.scheduleWritePackageRestrictions(userId);
+        mPm.scheduleWritePackageRestrictions(targetUserId);
         final Computer newSnapshot = mPm.snapshotComputer();
         if (!unsuspendedPackages.isEmpty()) {
             final String[] packageArray = unsuspendedPackages.toArray(
                     new String[unsuspendedPackages.size()]);
             mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
-                    false, userId);
+                    false, targetUserId);
             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
                     Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    packageArray, unsuspendedUids.toArray(), false, userId);
+                    packageArray, unsuspendedUids.toArray(), false, targetUserId);
         }
     }
 
@@ -404,7 +409,7 @@
      * @return The name of suspending package.
      */
     @Nullable
-    String getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
+    UserPackage getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
             int userId, int callingUid) {
         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
                 suspendedPackage, callingUid);
@@ -417,13 +422,13 @@
             return null;
         }
 
-        String suspendingPackage = null;
-        String suspendedBySystem = null;
-        String qasPackage = null;
+        UserPackage suspendingPackage = null;
+        UserPackage suspendedBySystem = null;
+        UserPackage qasPackage = null;
         for (int i = 0; i < userState.getSuspendParams().size(); i++) {
             suspendingPackage = userState.getSuspendParams().keyAt(i);
             var suspendParams = userState.getSuspendParams().valueAt(i);
-            if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+            if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) {
                 suspendedBySystem = suspendingPackage;
             }
             if (suspendParams.isQuarantined() && qasPackage == null) {
@@ -451,7 +456,7 @@
      */
     @Nullable
     SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot,
-            @NonNull String suspendedPackage, @NonNull String suspendingPackage, int userId,
+            @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId,
             int callingUid) {
         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
                 suspendedPackage, callingUid);
@@ -464,7 +469,7 @@
             return null;
         }
 
-        final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
+        final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
                 userState.getSuspendParams();
         if (suspendParamsMap == null) {
             return null;
@@ -493,34 +498,36 @@
      * be suspended or not.
      *
      * @param packageNames  The package names to check suspendability for.
-     * @param userId The user to check in
+     * @param targetUserId The user to check in
      * @param callingUid The caller's uid.
      * @return An array containing results of the checks
      */
     @NonNull
     boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames,
-            int userId, int callingUid) {
+            int targetUserId, int callingUid) {
         final boolean[] canSuspend = new boolean[packageNames.length];
-        final boolean isCallerOwner = isCallerDeviceOrProfileOwner(snapshot, userId, callingUid);
+        final boolean isCallerOwner =
+                isCallerDeviceOrProfileOwner(snapshot, targetUserId, callingUid);
         final long token = Binder.clearCallingIdentity();
         try {
             final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
-            final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId);
-            final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId);
+            final String activeLauncherPackageName =
+                    defaultAppProvider.getDefaultHome(targetUserId);
+            final String dialerPackageName = defaultAppProvider.getDefaultDialer(targetUserId);
             final String requiredInstallerPackage =
-                    getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, userId);
+                    getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, targetUserId);
             final String requiredUninstallerPackage =
-                    getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, userId);
+                    getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, targetUserId);
             final String requiredVerifierPackage =
-                    getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, userId);
+                    getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, targetUserId);
             final String requiredPermissionControllerPackage =
                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
-                            userId);
+                            targetUserId);
             for (int i = 0; i < packageNames.length; i++) {
                 canSuspend[i] = false;
                 final String packageName = packageNames[i];
 
-                if (mPm.isPackageDeviceAdmin(packageName, userId)) {
+                if (mPm.isPackageDeviceAdmin(packageName, targetUserId)) {
                     Slog.w(TAG, "Cannot suspend package \"" + packageName
                             + "\": has an active device admin");
                     continue;
@@ -555,12 +562,12 @@
                             + "\": required for permissions management");
                     continue;
                 }
-                if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                if (mProtectedPackages.isPackageStateProtected(targetUserId, packageName)) {
                     Slog.w(TAG, "Cannot suspend package \"" + packageName
                             + "\": protected package");
                     continue;
                 }
-                if (!isCallerOwner && snapshot.getBlockUninstall(userId, packageName)) {
+                if (!isCallerOwner && snapshot.getBlockUninstall(targetUserId, packageName)) {
                     Slog.w(TAG, "Cannot suspend package \"" + packageName
                             + "\": blocked by admin");
                     continue;
@@ -572,7 +579,7 @@
                 PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
                 AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
                 if (pkg != null) {
-                    final int uid = UserHandle.getUid(userId, packageState.getAppId());
+                    final int uid = UserHandle.getUid(targetUserId, packageState.getAppId());
                     // Cannot suspend SDK libs as they are controlled by SDK manager.
                     if (pkg.isSdkLibrary()) {
                         Slog.w(TAG, "Cannot suspend package: " + packageName
@@ -614,20 +621,6 @@
                         == AppOpsManager.MODE_ALLOWED;
     }
 
-    /**
-     * Suspends packages on behalf of an admin.
-     *
-     * @return array of packages that are unsuspendable, either because admin is not allowed to
-     * suspend them (e.g. current dialer) or there was other problem (e.g. package not found).
-     */
-    public String[] setPackagesSuspendedByAdmin(
-            Computer snapshot, int userId, String[] packageNames, boolean suspend) {
-        return setPackagesSuspended(snapshot, packageNames, suspend,
-                null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
-                PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
-                false /* quarantined */);
-    }
-
     private String getKnownPackageName(@NonNull Computer snapshot,
             @KnownPackages.KnownPackage int knownPackage, int userId) {
         final String[] knownPackages =
@@ -635,14 +628,15 @@
         return knownPackages.length > 0 ? knownPackages[0] : null;
     }
 
-    private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int userId,
+    private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int targetUserId,
             int callingUid) {
         if (callingUid == SYSTEM_UID) {
             return true;
         }
-        final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
+        final String ownerPackage =
+                mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
         if (ownerPackage != null) {
-            return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, userId,
+            return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, targetUserId,
                     callingUid);
         }
         return false;
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 7386301..14db70e 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -306,7 +306,6 @@
                 .setDarkThemeBadgeColors(
                         R.color.white)
                 .setDefaultRestrictions(getDefaultProfileRestrictions())
-                .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
                 .setDefaultUserProperties(new UserProperties.Builder()
                         .setStartWithParent(true)
                         .setCredentialShareableWithParent(true)
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 459e2cf..79c9c8e 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -28,9 +28,9 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.ApexManager;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 
 import libcore.io.IoUtils;
 
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 b23dbee..d0fe964 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -28,6 +28,7 @@
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
+import android.content.pm.Flags;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
@@ -52,6 +53,9 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.internal.pm.pkg.component.ComponentParseUtils;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedAttribution;
 import com.android.internal.pm.pkg.component.ParsedComponent;
@@ -63,11 +67,12 @@
 import com.android.internal.pm.pkg.component.ParsedProvider;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
 import com.android.server.pm.PackageArchiver;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUnserialized;
@@ -75,9 +80,6 @@
 import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.PackageUserStateUtils;
 import com.android.server.pm.pkg.SELinuxUtil;
-import com.android.server.pm.pkg.component.ComponentParseUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -273,8 +275,8 @@
                 final ActivityInfo[] res = new ActivityInfo[N];
                 for (int i = 0; i < N; i++) {
                     final ParsedActivity a = pkg.getActivities().get(i);
-                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a,
-                            aflags)) {
+                    if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
+                            a.isEnabled(), a.isDirectBootAware(), a.getName(), aflags)) {
                         if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
                                 a.getName())) {
                             continue;
@@ -293,8 +295,8 @@
                 final ActivityInfo[] res = new ActivityInfo[size];
                 for (int i = 0; i < size; i++) {
                     final ParsedActivity a = pkg.getReceivers().get(i);
-                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a,
-                            flags)) {
+                    if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
+                            a.isEnabled(), a.isDirectBootAware(), a.getName(), flags)) {
                         res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
                                 userId, pkgSetting);
                     }
@@ -309,8 +311,8 @@
                 final ServiceInfo[] res = new ServiceInfo[size];
                 for (int i = 0; i < size; i++) {
                     final ParsedService s = pkg.getServices().get(i);
-                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), s,
-                            flags)) {
+                    if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
+                            s.isEnabled(), s.isDirectBootAware(), s.getName(), flags)) {
                         res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo,
                                 userId, pkgSetting);
                     }
@@ -326,8 +328,8 @@
                 for (int i = 0; i < size; i++) {
                     final ParsedProvider pr = pkg.getProviders()
                             .get(i);
-                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), pr,
-                            flags)) {
+                    if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(),
+                            pr.isEnabled(), pr.isDirectBootAware(), pr.getName(), flags)) {
                         res[num++] = generateProviderInfo(pkg, pr, flags, state, applicationInfo,
                                 userId, pkgSetting);
                     }
@@ -473,7 +475,34 @@
         }
         info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
                 ? null : usesLibraryFiles.toArray(new String[0]);
-        info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+
+
+        if (!Flags.sdkLibIndependence()) {
+            info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+            info.optionalSharedLibraryInfos = null;
+        } else {
+            // sharedLibraryInfos contains all shared libraries that the app depends on (including
+            // the optional sdk libraries)
+            info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+            String[] libsNames = pkgSetting.getUsesSdkLibraries();
+            boolean[] libsOptional = pkgSetting.getUsesSdkLibrariesOptional();
+            List<SharedLibraryInfo> optionalSdkLibraries = null;
+            if (!ArrayUtils.isEmpty(libsOptional) && !ArrayUtils.isEmpty(libsNames)
+                    && libsNames.length == libsOptional.length) {
+                for (SharedLibraryInfo info1 : usesLibraryInfos) {
+                    if (info1.getType() == SharedLibraryInfo.TYPE_SDK_PACKAGE) {
+                        int index = ArrayUtils.indexOf(libsNames, info1.getName());
+                        if (index >= 0 && libsOptional[index]) {
+                            if (optionalSdkLibraries == null) {
+                                optionalSdkLibraries = new ArrayList<>();
+                            }
+                            optionalSdkLibraries.add(info1);
+                        }
+                    }
+                }
+            }
+            info.optionalSharedLibraryInfos = optionalSdkLibraries;
+        }
         if (info.category == ApplicationInfo.CATEGORY_UNDEFINED) {
             info.category = pkgSetting.getCategoryOverride();
         }
@@ -923,7 +952,7 @@
                 | flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
                 | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
                 | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
-                | flag(AndroidPackageUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM)
+                | flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM)
                 | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST);
 
         return appInfoFlags(pkgWithoutStateFlags, pkgSetting);
@@ -964,12 +993,12 @@
                 | flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
                 | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
                 | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING)
-                | flag(AndroidPackageUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
-                | flag(AndroidPackageUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
-                | flag(AndroidPackageUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM)
-                | flag(AndroidPackageUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR)
-                | flag(AndroidPackageUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
-                | flag(AndroidPackageUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM)
+                | flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
+                | flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+                | flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM)
+                | flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR)
+                | flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
+                | flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM)
                 | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
 
         Boolean resizeableActivity = pkg.getResizeableActivity();
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 1c751e0..b6a08a5 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -35,17 +35,19 @@
 import android.util.Slog;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
 import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import java.io.File;
 import java.util.List;
+import java.util.Set;
 
 /**
  * The v2 of package parsing for use when parsing is initiated in the server and must
@@ -88,6 +90,16 @@
                 // behavior.
                 return false;
             }
+
+            @Override
+            public Set<String> getHiddenApiWhitelistedApps() {
+                return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
+            }
+
+            @Override
+            public Set<String> getInstallConstraintsAllowlist() {
+                return SystemConfig.getInstance().getInstallConstraintsAllowlist();
+            }
         });
     }
 
@@ -221,7 +233,7 @@
                 @NonNull String baseCodePath, @NonNull String codePath,
                 @NonNull TypedArray manifestArray, boolean isCoreApp) {
             return PackageImpl.forParsing(packageName, baseCodePath, codePath, manifestArray,
-                    isCoreApp);
+                    isCoreApp, Callback.this);
         }
 
         /**
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 61be6e1..1b7c7ad 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -29,7 +29,9 @@
 import android.os.incremental.IncrementalManager;
 
 import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.parsing.PackageParserException;
 import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
 import com.android.internal.pm.pkg.component.ParsedProvider;
@@ -37,7 +39,6 @@
 import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
-import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -134,10 +135,10 @@
     /**
      * Validate the dex metadata files installed for the given package.
      *
-     * @throws PackageManagerException in case of errors.
+     * @throws PackageParserException in case of errors.
      */
     public static void validatePackageDexMetadata(AndroidPackage pkg)
-            throws PackageManagerException {
+            throws PackageParserException {
         Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
         String packageName = pkg.getPackageName();
         long versionCode = pkg.getLongVersionCode();
@@ -146,7 +147,7 @@
             final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
                     input.reset(), dexMetadata, packageName, versionCode);
             if (result.isError()) {
-                throw new PackageManagerException(
+                throw new PackageParserException(
                         result.getErrorCode(), result.getErrorMessage(), result.getException());
             }
         }
@@ -314,60 +315,4 @@
         info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
         info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
     }
-
-    /**
-     * @deprecated Use {@link PackageState#isSystem}
-     */
-    @Deprecated
-    public static boolean isSystem(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isSystem();
-    }
-
-    /**
-     * @deprecated Use {@link PackageState#isSystemExt}
-     */
-    @Deprecated
-    public static boolean isSystemExt(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isSystemExt();
-    }
-
-    /**
-     * @deprecated Use {@link PackageState#isPrivileged}
-     */
-    @Deprecated
-    public static boolean isPrivileged(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isPrivileged();
-    }
-
-    /**
-     * @deprecated Use {@link PackageState#isOem}
-     */
-    @Deprecated
-    public static boolean isOem(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isOem();
-    }
-
-    /**
-     * @deprecated Use {@link PackageState#isVendor}
-     */
-    @Deprecated
-    public static boolean isVendor(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isVendor();
-    }
-
-    /**
-     * @deprecated Use {@link PackageState#isProduct}
-     */
-    @Deprecated
-    public static boolean isProduct(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isProduct();
-    }
-
-    /**
-     * @deprecated Use {@link PackageState#isOdm}
-     */
-    @Deprecated
-    public static boolean isOdm(@NonNull AndroidPackage pkg) {
-        return ((AndroidPackageHidden) pkg).isOdm();
-    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 8bd2d94..671e031 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -120,8 +120,11 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.RoSystemProperties;
+import com.android.internal.pm.permission.CompatibilityPermissionInfo;
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.IntPair;
@@ -144,8 +147,6 @@
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedPermissionUtils;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.SoftRestrictedPermissionPolicy;
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 2f4ad2d8..15b693c 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
 import android.content.pm.overlay.OverlayPaths;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -173,7 +174,7 @@
 
     @Nullable
     @Override
-    public WatchedArrayMap<String, SuspendParams> getSuspendParams() {
+    public WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams() {
         return null;
     }
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index c5ef525..7a5a14d 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
 import android.content.pm.overlay.OverlayPaths;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -121,7 +122,7 @@
      * Suspending package to suspend params
      */
     @Nullable
-    private WatchedArrayMap<String, SuspendParams> mSuspendParams;
+    private WatchedArrayMap<UserPackage, SuspendParams> mSuspendParams;
 
     @Nullable
     private WatchedArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
@@ -369,7 +370,10 @@
         return !CollectionUtils.isEmpty(mSuspendParams);
     }
 
-    public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage,
+    /**
+     * Adds or updates suspension params by the given package.
+     */
+    public PackageUserStateImpl putSuspendParams(@NonNull UserPackage suspendingPackage,
             @Nullable SuspendParams suspendParams) {
         if (mSuspendParams == null) {
             mSuspendParams = new WatchedArrayMap<>();
@@ -384,7 +388,10 @@
         return this;
     }
 
-    public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) {
+    /**
+     * Removes suspension by the given package.
+     */
+    public PackageUserStateImpl removeSuspension(@NonNull UserPackage suspendingPackage) {
         if (mSuspendParams != null) {
             mSuspendParams.remove(suspendingPackage);
             onChanged();
@@ -565,7 +572,7 @@
      * Suspending package to suspend params
      */
     public @NonNull PackageUserStateImpl setSuspendParams(
-            @NonNull ArrayMap<String, SuspendParams> value) {
+            @NonNull ArrayMap<UserPackage, SuspendParams> value) {
         if (value == null) {
             return this;
         }
@@ -778,7 +785,7 @@
      * Suspending package to suspend params
      */
     @DataClass.Generated.Member
-    public @Nullable WatchedArrayMap<String,SuspendParams> getSuspendParams() {
+    public @Nullable WatchedArrayMap<UserPackage,SuspendParams> getSuspendParams() {
         return mSuspendParams;
     }
 
@@ -830,7 +837,7 @@
      * Suspending package to suspend params
      */
     @DataClass.Generated.Member
-    public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<String,SuspendParams> value) {
+    public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<UserPackage,SuspendParams> value) {
         mSuspendParams = value;
         return this;
     }
@@ -909,10 +916,10 @@
     }
 
     @DataClass.Generated(
-            time = 1701470095849L,
+            time = 1701864813354L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
-            inputSignatures = "private  int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  long mDeDataInode\nprivate  int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate  boolean watchableEquals(com.android.server.utils.Watchable)\nprivate  int watchableHashCode()\nprivate  boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate  int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final  int INSTALLED\nprivate static final  int STOPPED\nprivate static final  int NOT_LAUNCHED\nprivate static final  int HIDDEN\nprivate static final  int INSTANT_APP\nprivate static final  int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+            inputSignatures = "private  int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  long mDeDataInode\nprivate  int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(android.content.pm.UserPackage)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate  boolean watchableEquals(com.android.server.utils.Watchable)\nprivate  int watchableHashCode()\nprivate  boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate  int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final  int INSTALLED\nprivate static final  int STOPPED\nprivate static final  int NOT_LAUNCHED\nprivate static final  int HIDDEN\nprivate static final  int INSTANT_APP\nprivate static final  int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index 46cc830..f8d745c 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.pm.UserPackage;
 import android.content.pm.pkg.FrameworkPackageUserState;
 import android.util.Pair;
 
@@ -38,7 +39,7 @@
 
     // TODO: Make non-null with emptyMap()
     @Nullable
-    WatchedArrayMap<String, SuspendParams> getSuspendParams();
+    WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams();
 
     @Nullable
     WatchedArraySet<String> getDisabledComponentsNoCopy();
diff --git a/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java
index 6cbc1de..6a15641 100644
--- a/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java
+++ b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm.pkg;
 
+import com.android.internal.pm.pkg.SEInfoUtil;
+
 /**
  * Utility methods that need to be used in application space.
  * @hide
@@ -23,10 +25,10 @@
 public final class SELinuxUtil {
 
     /** Append to existing seinfo label for instant apps @hide */
-    private static final String INSTANT_APP_STR = ":ephemeralapp";
+    private static final String INSTANT_APP_STR = SEInfoUtil.INSTANT_APP_STR;
 
     /** Append to existing seinfo when modifications are complete @hide */
-    public static final String COMPLETE_STR = ":complete";
+    public static final String COMPLETE_STR = SEInfoUtil.COMPLETE_STR;
 
     /** @hide */
     public static String getSeinfoUser(PackageUserState userState) {
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 8430cf7..253eb40 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
 import android.content.pm.overlay.OverlayPaths;
 import android.util.ArraySet;
 
@@ -349,7 +350,7 @@
 
             @NonNull
             @Override
-            public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+            public PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage,
                     @Nullable SuspendParams suspendParams) {
                 if (mUserState != null) {
                     mUserState.putSuspendParams(suspendingPackage, suspendParams);
@@ -359,7 +360,7 @@
 
             @NonNull
             @Override
-            public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) {
+            public PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage) {
                 if (mUserState != null) {
                     mUserState.removeSuspension(suspendingPackage);
                 }
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index 0c6c672..f6b2104 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
+import android.content.pm.UserPackage;
 import android.content.pm.overlay.OverlayPaths;
 
 import com.android.server.pm.pkg.PackageUserStateImpl;
@@ -38,11 +39,11 @@
             @PackageManager.DistractionRestriction int restrictionFlags);
 
     @NonNull
-    PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+    PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage,
             @Nullable SuspendParams suspendParams);
 
     @NonNull
-    PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage);
+    PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage);
 
     @NonNull
     PackageUserStateWrite setHidden(boolean hidden);
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 532a7f8..c9da99d 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -45,11 +45,13 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ComponentMutateUtils;
 import com.android.internal.pm.pkg.component.ParsedActivity;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedProviderImpl;
 import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.IntentResolver;
@@ -62,8 +64,6 @@
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6f27507..d903ad4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -41,7 +41,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
-import android.content.res.Resources;
 import android.graphics.Rect;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
@@ -2081,6 +2080,45 @@
         }
 
         @Override
+        public void stopPlayback(IBinder sessionToken, int mode, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "stopPlayback");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).stopPlayback(
+                                mode);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in stopPlayback(mode)", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void startPlayback(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "stopPlayback");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in startPlayback()", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index f9d344b..1b45c1b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -48,6 +48,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -335,19 +336,19 @@
             return false;
         }
         final String suspendedPackage = mAInfo.applicationInfo.packageName;
-        final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
-        if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+        final UserPackage suspender = pmi.getSuspendingPackage(suspendedPackage, mUserId);
+        if (suspender != null && PLATFORM_PACKAGE_NAME.equals(suspender.packageName)) {
             return interceptSuspendedByAdminPackage();
         }
         final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage,
-                suspendingPackage, mUserId);
+                suspender, mUserId);
         final Bundle crossProfileOptions = hasCrossProfileAnimation()
                 ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
                 : null;
         final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
                 FLAG_IMMUTABLE);
         mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
-                suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId);
+                suspender, dialogInfo, crossProfileOptions, target, mUserId);
         mCallingPid = mRealCallingPid;
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 4a3d0c1..32d60c5 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static com.android.input.flags.Flags.enablePointerChoreographer;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -23,6 +24,7 @@
 import android.annotation.NonNull;
 import android.content.ClipData;
 import android.content.Context;
+import android.hardware.input.InputManagerGlobal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -31,6 +33,8 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindow;
+import android.view.InputDevice;
+import android.view.PointerIcon;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -97,8 +101,8 @@
     }
 
     IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags,
-            SurfaceControl surface, int touchSource, float touchX, float touchY,
-            float thumbCenterX, float thumbCenterY, ClipData data) {
+            SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId,
+            float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
         if (DEBUG_DRAG) {
             Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
                     Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + ","
@@ -208,7 +212,17 @@
 
                 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
                 mDragState.broadcastDragStartedLocked(touchX, touchY);
-                mDragState.overridePointerIconLocked(touchSource);
+                if (enablePointerChoreographer()) {
+                    if ((touchSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+                        InputManagerGlobal.getInstance().setPointerIcon(
+                                PointerIcon.getSystemIcon(
+                                        mService.mContext, PointerIcon.TYPE_GRABBING),
+                                mDragState.mDisplayContent.getDisplayId(), touchDeviceId,
+                                touchPointerId, mDragState.getInputToken());
+                    }
+                } else {
+                    mDragState.overridePointerIconLocked(touchSource);
+                }
                 // remember the thumb offsets for later
                 mDragState.mThumbOffsetX = thumbCenterX;
                 mDragState.mThumbOffsetY = thumbCenterY;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a888f84..adbe3bc 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -416,6 +416,13 @@
         return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle;
     }
 
+    IBinder getInputToken() {
+        if (mInputInterceptor == null || mInputInterceptor.mClientChannel == null) {
+            return null;
+        }
+        return mInputInterceptor.mClientChannel.getToken();
+    }
+
     /**
      * @param display The Display that the window being dragged is on.
      */
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 0d15fc9..2b841fd 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 
 import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
@@ -110,15 +111,6 @@
             return;
         }
 
-        final TransitionRequestInfo.DisplayChange displayChange =
-                new TransitionRequestInfo.DisplayChange(displayId);
-
-        final Rect startAbsBounds = new Rect(0, 0, oldDisplayWidth, oldDisplayHeight);
-        displayChange.setStartAbsBounds(startAbsBounds);
-        final Rect endAbsBounds = new Rect(0, 0, newDisplayWidth, newDisplayHeight);
-        displayChange.setEndAbsBounds(endAbsBounds);
-        displayChange.setPhysicalDisplayChanged(true);
-
         mTransition = null;
 
         if (mTransitionController.isCollecting()) {
@@ -128,10 +120,20 @@
 
             // Make sure that transition is not ready until we finish the remote display change
             mTransition.setReady(mDisplayContent, false);
+            mTransition.addFlag(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH);
 
             ProtoLog.d(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                     "Adding display switch to existing collecting transition");
         } else {
+            final TransitionRequestInfo.DisplayChange displayChange =
+                    new TransitionRequestInfo.DisplayChange(displayId);
+
+            final Rect startAbsBounds = new Rect(0, 0, oldDisplayWidth, oldDisplayHeight);
+            displayChange.setStartAbsBounds(startAbsBounds);
+            final Rect endAbsBounds = new Rect(0, 0, newDisplayWidth, newDisplayHeight);
+            displayChange.setEndAbsBounds(endAbsBounds);
+            displayChange.setPhysicalDisplayChanged(true);
+
             mTransition = mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE,
                     0 /* flags */,
                     mDisplayContent, mDisplayContent, null /* remoteTransition */,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 56f9aa4..57939bc 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -340,7 +340,8 @@
 
     @Override
     public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
-            float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
+            int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX,
+            float thumbCenterY, ClipData data) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         // Validate and resolve ClipDescription data before clearing the calling identity
@@ -349,7 +350,8 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
-                    touchX, touchY, thumbCenterX, thumbCenterY, data);
+                    touchDeviceId, touchPointerId, touchX, touchY, thumbCenterX, thumbCenterY,
+                    data);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index d66b9b9..2a0f1e2 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -468,7 +468,6 @@
                      borrowed_fd incomingFd, bool waitOnEof, std::vector<char>* buffer,
                      std::vector<IncFsDataBlock>* blocks) {
         IncFsSize remaining = size;
-        IncFsSize totalSize = 0;
         IncFsBlockIndex blockIdx = 0;
         while (remaining > 0) {
             constexpr auto capacity = BUFFER_SIZE;
@@ -502,7 +501,6 @@
 
             buffer->resize(size + read);
             remaining -= read;
-            totalSize += read;
         }
         if (!buffer->empty() && !flashToIncFs(incfsFd, kind, true, &blockIdx, buffer, blocks)) {
             return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e0a2f30..a490013 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2469,7 +2469,7 @@
     private void migratePersonalAppSuspensionLocked(
             int doUserId, int poUserId, ActiveAdmin poAdmin) {
         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
-        if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) {
+        if (!pmi.isAdminSuspendingAnyPackages(doUserId)) {
             Slogf.i(LOG_TAG, "DO is not suspending any apps.");
             return;
         }
@@ -2480,7 +2480,7 @@
             poAdmin.mSuspendPersonalApps = true;
         } else {
             Slogf.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
-            pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId);
+            pmi.unsuspendAdminSuspendedPackages(doUserId);
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 6570ce1..506dbe8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -16,8 +16,6 @@
 
 package com.android.server.devicepolicy;
 
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppGlobals;
@@ -287,7 +285,7 @@
                 suspendPersonalAppsInPackageManager(context, userId);
             } else {
                 LocalServices.getService(PackageManagerInternal.class)
-                        .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, userId);
+                        .unsuspendAdminSuspendedPackages(userId);
             }
         });
         return true;
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 02032c7..f69f628 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -24,6 +24,7 @@
 import android.os.Build
 import android.util.Slog
 import com.android.internal.os.RoSystemProperties
+import com.android.internal.pm.permission.CompatibilityPermissionInfo
 import com.android.modules.utils.BinaryXmlPullParser
 import com.android.modules.utils.BinaryXmlSerializer
 import com.android.server.permission.access.AccessState
@@ -42,7 +43,6 @@
 import com.android.server.permission.access.util.isInternal
 import com.android.server.pm.KnownPackages
 import com.android.server.pm.parsing.PackageInfoUtils
-import com.android.server.pm.permission.CompatibilityPermissionInfo
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageState
 
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index d62da1a..5b222c0 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -57,11 +57,11 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 import com.android.server.LocalServices;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowUserManager;
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 3011fa1..5c4716d 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -25,6 +25,7 @@
 import android.os.UserHandle
 import android.util.ArrayMap
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.PackageImpl
 import com.android.internal.pm.parsing.pkg.ParsedPackage
 import com.android.internal.pm.pkg.component.ParsedActivity
 import com.android.server.pm.AppsFilterImpl
@@ -38,7 +39,6 @@
 import com.android.server.pm.SharedLibrariesImpl
 import com.android.server.pm.UserManagerInternal
 import com.android.server.pm.UserManagerService
-import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.snapshot.PackageDataSnapshot
@@ -49,6 +49,8 @@
 import com.android.server.testutils.whenever
 import com.android.server.wm.ActivityTaskManagerInternal
 import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.util.UUID
 import org.junit.After
 import org.junit.Before
 import org.junit.BeforeClass
@@ -61,8 +63,6 @@
 import org.mockito.Mockito.intThat
 import org.mockito.Mockito.same
 import org.testng.Assert.assertThrows
-import java.io.File
-import java.util.UUID
 
 @RunWith(Parameterized::class)
 class PackageManagerComponentLabelIconOverrideTest {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 3461bb6..7277fd7 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -49,20 +49,20 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedActivityImpl;
+import com.android.internal.pm.pkg.component.ParsedComponentImpl;
+import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl;
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl;
 import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionImpl;
+import com.android.internal.pm.pkg.component.ParsedProviderImpl;
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.om.OverlayReferenceMapper;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedComponentImpl;
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedPermissionImpl;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
 import com.android.server.utils.WatchableTester;
 
 import org.junit.Before;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java
index f0d389b..b1c3e94 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java
@@ -32,11 +32,11 @@
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.PackageStateUnserialized;
 import com.android.server.pm.pkg.PackageUserStateImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import org.junit.After;
 import org.junit.Before;
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 f07e820..40d3d5c 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
@@ -45,15 +45,19 @@
 import android.app.PropertyInvalidatedCache;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
 import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.os.BaseBundle;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -63,10 +67,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.permission.persistence.RuntimePermissionsPersistence;
 import com.android.server.LocalServices;
-import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.ArchiveState;
@@ -85,6 +90,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -109,6 +115,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PackageManagerSettingsTests {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private static final String PACKAGE_NAME_1 = "com.android.app1";
     private static final String PACKAGE_NAME_2 = "com.android.app2";
     private static final String PACKAGE_NAME_3 = "com.android.app3";
@@ -140,6 +149,7 @@
     public void setup() {
         // Disable binder caches in this process.
         PropertyInvalidatedCache.disableForTestMode();
+
     }
 
     @Before
@@ -161,6 +171,107 @@
         deleteFolder(InstrumentationRegistry.getContext().getFilesDir());
     }
 
+    @Test
+    public void testApplicationInfoForUseSdkOptionalEnabled() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_SDK_LIB_INDEPENDENCE);
+
+        // Create basic information for SDK lib
+        final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+        ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
+                .setUid(ps1.getAppId())
+                .setSystem(true)
+                .hideAsFinal());
+        ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" });
+        ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 });
+        ps1.setUsesSdkLibrariesOptional(new boolean[] {true});
+        ps1.addUsesLibraryInfo(new SharedLibraryInfo("path1",
+                "packageName1",
+                Collections.emptyList(),
+                "com.example.sdk.one",
+                12 /*version*/,
+                SharedLibraryInfo.TYPE_SDK_PACKAGE,
+                null /*declaringPackage*/,
+                null /*dependentPackages*/,
+                null /*dependencies*/,
+                false /*isNative*/));
+        ps1.addUsesLibraryInfo(new SharedLibraryInfo("path11",
+                "packageName11",
+                Collections.emptyList(),
+                "com.example.sdk.oneone",
+                1212 /*version*/,
+                SharedLibraryInfo.TYPE_STATIC,
+                null /*declaringPackage*/,
+                null /*dependentPackages*/,
+                null /*dependencies*/,
+                false /*isNative*/));
+        ApplicationInfo appInfo1 = PackageInfoUtils.generateApplicationInfo(ps1.getAndroidPackage(),
+                0 /*flags*/, ps1.getUserStateOrDefault(0), 0 /*userId*/,
+                ps1);
+        assertThat(appInfo1, notNullValue());
+        assertThat(appInfo1.sharedLibraryInfos, notNullValue());
+        assertThat(appInfo1.optionalSharedLibraryInfos, notNullValue());
+        assertEquals(appInfo1.sharedLibraryInfos.get(0).getName(), "com.example.sdk.one");
+        assertEquals(appInfo1.optionalSharedLibraryInfos.get(0).getName(), "com.example.sdk.one");
+
+        final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+        ps2.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed())
+                .setUid(ps2.getAppId())
+                .setSystem(true)
+                .hideAsFinal());
+        ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" });
+        ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 });
+        ps2.setUsesSdkLibrariesOptional(new boolean[] {false});
+        ps2.addUsesLibraryInfo(new SharedLibraryInfo("path2",
+                "packageName2",
+                Collections.emptyList(),
+                "com.example.sdk.two",
+                34 /*version*/,
+                SharedLibraryInfo.TYPE_SDK_PACKAGE,
+                null /*declaringPackage*/,
+                null /*dependentPackages*/,
+                null /*dependencies*/,
+                false /*isNative*/));
+        ApplicationInfo appInfo2 = PackageInfoUtils.generateApplicationInfo(ps2.getAndroidPackage(),
+                0 /*flags*/, ps2.getUserStateOrDefault(0), 0 /*userId*/,
+                ps2);
+        assertThat(appInfo2, notNullValue());
+        assertThat(appInfo2.sharedLibraryInfos, notNullValue());
+        assertThat(appInfo2.optionalSharedLibraryInfos, nullValue());
+        assertEquals(appInfo2.sharedLibraryInfos.get(0).getName(), "com.example.sdk.two");
+    }
+
+    @Test
+    public void testApplicationInfoForUseSdkOptionalDisabled() throws Exception {
+        mSetFlagsRule.disableFlags(Flags.FLAG_SDK_LIB_INDEPENDENCE);
+
+        // Create basic information for SDK lib
+        final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+        ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
+                .setUid(ps1.getAppId())
+                .setSystem(true)
+                .hideAsFinal());
+        ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" });
+        ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 });
+        ps1.setUsesSdkLibrariesOptional(new boolean[] {true});
+        ps1.addUsesLibraryInfo(new SharedLibraryInfo("path1",
+                "packageName1",
+                Collections.emptyList(),
+                "com.example.sdk.one",
+                12 /*version*/,
+                SharedLibraryInfo.TYPE_SDK_PACKAGE,
+                null /*declaringPackage*/,
+                null /*dependentPackages*/,
+                null /*dependencies*/,
+                false /*isNative*/));
+        ApplicationInfo appInfo1 = PackageInfoUtils.generateApplicationInfo(ps1.getAndroidPackage(),
+                0 /*flags*/, ps1.getUserStateOrDefault(0), 0 /*userId*/,
+                ps1);
+        assertThat(appInfo1, notNullValue());
+        assertThat(appInfo1.sharedLibraryInfos, notNullValue());
+        assertThat(appInfo1.optionalSharedLibraryInfos, nullValue());
+        assertEquals(appInfo1.sharedLibraryInfos.get(0).getName(), "com.example.sdk.one");
+    }
+
     /** make sure our initialized KeySetManagerService metadata matches packages.xml */
     @Test
     public void testReadKeySetSettings() throws Exception {
@@ -315,7 +426,7 @@
         PackageUserStateInternal packageUserState1 = ps1.readUserState(0);
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
-        assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
+        assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android")));
         assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
         assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
                 is(nullValue()));
@@ -327,7 +438,7 @@
         packageUserState1 = ps1.readUserState(0);
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
-        assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
+        assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android")));
         assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
         assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
                 is(nullValue()));
@@ -362,7 +473,8 @@
         watcher.verifyNoChangeReported("readUserState");
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
-        assertThat(packageUserState1.getSuspendParams().keyAt(0), is(PACKAGE_NAME_3));
+        assertThat(packageUserState1.getSuspendParams().keyAt(0),
+                is(UserPackage.of(0, PACKAGE_NAME_3)));
         final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0);
         watcher.verifyNoChangeReported("fetch user state");
         assertThat(params, is(notNullValue()));
@@ -413,19 +525,24 @@
                 .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
                 .build();
 
-        ps1.modifyUserState(0).putSuspendParams("suspendingPackage1",
+        UserPackage suspender1 = UserPackage.of(0, "suspendingPackage1");
+        UserPackage suspender2 = UserPackage.of(0, "suspendingPackage2");
+        UserPackage suspender3 = UserPackage.of(0, "suspendingPackage3");
+        UserPackage irrelevantSuspender = UserPackage.of(0, "irrelevant");
+
+        ps1.modifyUserState(0).putSuspendParams(suspender1,
                 new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
-        ps1.modifyUserState(0).putSuspendParams("suspendingPackage2",
+        ps1.modifyUserState(0).putSuspendParams(suspender2,
                 new SuspendParams(dialogInfo2, appExtras2, launcherExtras2));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
         watcher.verifyChangeReported("put package 1");
 
-        ps2.modifyUserState(0).putSuspendParams("suspendingPackage3",
+        ps2.modifyUserState(0).putSuspendParams(suspender3,
                 new SuspendParams(null, appExtras1, null));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
         watcher.verifyChangeReported("put package 2");
 
-        ps3.modifyUserState(0).removeSuspension("irrelevant");
+        ps3.modifyUserState(0).removeSuspension(irrelevantSuspender);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
         watcher.verifyChangeReported("put package 3");
 
@@ -450,7 +567,7 @@
         assertThat(readPus1.getSuspendParams().size(), is(2));
         watcher.verifyNoChangeReported("read package param");
 
-        assertThat(readPus1.getSuspendParams().keyAt(0), is("suspendingPackage1"));
+        assertThat(readPus1.getSuspendParams().keyAt(0), is(suspender1));
         final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
         watcher.verifyNoChangeReported("read package param");
         assertThat(params11, is(notNullValue()));
@@ -460,7 +577,7 @@
                 is(true));
         watcher.verifyNoChangeReported("read package param");
 
-        assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2"));
+        assertThat(readPus1.getSuspendParams().keyAt(1), is(suspender2));
         final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
         assertThat(params12, is(notNullValue()));
         assertThat(params12.getDialogInfo(), is(dialogInfo2));
@@ -473,7 +590,7 @@
                 .readUserState(0);
         assertThat(readPus2.isSuspended(), is(true));
         assertThat(readPus2.getSuspendParams().size(), is(1));
-        assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3"));
+        assertThat(readPus2.getSuspendParams().keyAt(0), is(suspender3));
         final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
         assertThat(params21, is(notNullValue()));
         assertThat(params21.getDialogInfo(), is(nullValue()));
@@ -1024,7 +1141,8 @@
                 .setNeutralButtonText(0x11220003)
                 .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
                 .build();
-        origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
+        origPkgSetting01.modifyUserState(0).putSuspendParams(
+                UserPackage.of(0, "suspendingPackage1"),
                 new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
         origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01));
         final PackageSetting testPkgSetting01 = new PackageSetting(
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
index 9c48af8..285c059 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
@@ -73,7 +73,7 @@
 import com.android.compatibility.common.util.CddTest;
 import com.android.internal.content.InstallLocationUtils;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.parsing.ParsingUtils;
 import com.android.server.pm.test.service.server.R;
 
 import dalvik.system.VMRuntime;
@@ -346,7 +346,7 @@
 
     private ParsedPackage parsePackage(Uri packageURI) {
         final String archiveFilePath = packageURI.getPath();
-        ParseResult<ParsedPackage> result = ParsingPackageUtils.parseDefaultOneTime(
+        ParseResult<ParsedPackage> result = ParsingUtils.parseDefaultOneTime(
                 new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(),
                 false /*collectCertificates*/);
         if (result.isError()) {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index a62cd4f..03e45a2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.pm;
 
-import static com.android.server.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+import static com.android.internal.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -58,17 +58,28 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.permission.CompatibilityPermissionInfo;
 import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedActivityImpl;
 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl;
 import com.android.internal.pm.pkg.component.ParsedPermission;
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl;
+import com.android.internal.pm.pkg.component.ParsedPermissionImpl;
+import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
 import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedProviderImpl;
 import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedServiceImpl;
 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.parsing.PackageCacher;
@@ -76,19 +87,8 @@
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.TestPackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.permission.CompatibilityPermissionInfo;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
-import com.android.server.pm.pkg.component.ParsedPermissionImpl;
-import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedServiceImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -725,6 +725,16 @@
                 public boolean hasFeature(String feature) {
                     return false;
                 }
+
+                @Override
+                public Set<String> getHiddenApiWhitelistedApps() {
+                    return new ArraySet<>();
+                }
+
+                @Override
+                public Set<String> getInstallConstraintsAllowlist() {
+                    return new ArraySet<>();
+                }
             });
             if (cacheDir != null) {
                 setCacheDir(cacheDir);
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 c0c7032..9780440 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
@@ -27,6 +27,7 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserPackage;
 import android.content.pm.overlay.OverlayPaths;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
@@ -89,7 +90,7 @@
         assertThat(testUserState.equals(oldUserState), is(false));
 
         oldUserState = new PackageUserStateImpl();
-        oldUserState.putSuspendParams("suspendingPackage",
+        oldUserState.putSuspendParams(UserPackage.of(0, "suspendingPackage"),
                 new SuspendParams(null, new PersistableBundle(), null));
         assertThat(testUserState.equals(oldUserState), is(false));
 
@@ -220,6 +221,8 @@
         final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name",
                 "launcherExtras2", null, 0);
 
+        final int suspendingUser1 = 0;
+        final int suspendingUser2 = 10;
         final String suspendingPackage1 = "package1";
         final String suspendingPackage2 = "package2";
 
@@ -230,12 +233,12 @@
                 .setMessage("dialogMessage2")
                 .build();
 
-        final ArrayMap<String, SuspendParams> paramsMap1 = new ArrayMap<>();
-        paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1,
-                launcherExtras1));
-        final ArrayMap<String, SuspendParams> paramsMap2 = new ArrayMap<>();
-        paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2,
-                appExtras2, launcherExtras2));
+        final ArrayMap<UserPackage, SuspendParams> paramsMap1 = new ArrayMap<>();
+        paramsMap1.put(UserPackage.of(suspendingUser1, suspendingPackage1),
+                createSuspendParams(dialogInfo1, appExtras1, launcherExtras1));
+        final ArrayMap<UserPackage, SuspendParams> paramsMap2 = new ArrayMap<>();
+        paramsMap2.put(UserPackage.of(suspendingUser2, suspendingPackage2),
+                createSuspendParams(dialogInfo2, appExtras2, launcherExtras2));
 
 
         final PackageUserStateImpl testUserState1 = new PackageUserStateImpl();
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
index 6202908..c1271bb 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
@@ -50,13 +50,13 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import org.hamcrest.BaseMatcher;
@@ -376,7 +376,7 @@
         // Create the ParsedPackage for the apex
         final ParsedPackage basicPackage =
                 ((ParsedPackage) new PackageImpl(DUMMY_PACKAGE_NAME, codePath, codePath,
-                        mock(TypedArray.class), false)
+                        mock(TypedArray.class), false, null)
                         .setVolumeUuid(UUID_ONE.toString())
                         .hideAsParsed())
                         .setVersionCodeMajor(1)
@@ -595,7 +595,7 @@
         // TODO(b/135203078): Make this use PackageImpl.forParsing and separate the steps
         return (ParsingPackage) ((ParsedPackage) new PackageImpl(packageName,
                 "/data/tmp/randompath/base.apk", createCodePath(packageName),
-                mock(TypedArray.class), false)
+                mock(TypedArray.class), false, null)
                 .setVolumeUuid(UUID_ONE.toString())
                 .addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"})
                 .addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"})
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index b102ab4..b63950c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -39,14 +39,14 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.component.ParsedActivityUtils;
 import com.android.internal.pm.pkg.component.ParsedComponent;
 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
 import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedActivityUtils;
-import com.android.server.pm.pkg.component.ParsedPermissionUtils;
 import com.android.server.pm.test.service.server.R;
 
 import com.google.common.truth.Expect;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index 67b91d2..c435b94 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -22,7 +22,6 @@
 import android.platform.test.annotations.Presubmit
 import androidx.test.InstrumentationRegistry
 import com.android.internal.pm.parsing.pkg.ParsedPackage
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.test.service.server.R
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -121,7 +120,7 @@
                 input.copyTo(output)
             }
         }
-        return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(),
+        return ParsingUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(),
                 false /*collectCertificates*/)
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java
new file mode 100644
index 0000000..a9eac95
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.parsing;
+
+import android.annotation.NonNull;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.TypedArray;
+import android.permission.PermissionManager;
+
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.SystemConfig;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/** @hide **/
+public class ParsingUtils {
+
+    /**
+     * @see ParsingPackageUtils#parseDefault(ParseInput, File, int, List, boolean,
+     * ParsingPackageUtils.Callback)
+     */
+    @NonNull
+    public static ParseResult<ParsedPackage> parseDefaultOneTime(File file,
+            @ParsingPackageUtils.ParseFlags int parseFlags,
+            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
+            boolean collectCertificates) {
+        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
+        return ParsingPackageUtils.parseDefault(input, file, parseFlags, splitPermissions, collectCertificates,
+                new ParsingPackageUtils.Callback() {
+            @Override
+            public boolean hasFeature(String feature) {
+                // Assume the device doesn't support anything. This will affect permission
+                // parsing and will force <uses-permission/> declarations to include all
+                // requiredNotFeature permissions and exclude all requiredFeature
+                // permissions. This mirrors the old behavior.
+                return false;
+            }
+
+            @Override
+            public ParsingPackage startParsingPackage(
+                    @NonNull String packageName,
+                    @NonNull String baseApkPath,
+                    @NonNull String path,
+                    @NonNull TypedArray manifestArray, boolean isCoreApp) {
+                return PackageImpl.forParsing(packageName, baseApkPath, path, manifestArray,
+                        isCoreApp, this);
+            }
+
+            @Override
+            public Set<String> getHiddenApiWhitelistedApps() {
+                return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
+            }
+
+            @Override
+            public Set<String> getInstallConstraintsAllowlist() {
+                return SystemConfig.getInstance().getInstallConstraintsAllowlist();
+            }
+        });
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 1f57b6c..98af63c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -17,15 +17,15 @@
 package com.android.server.pm.parsing
 
 import android.content.pm.PackageManager
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import android.platform.test.annotations.Postsubmit
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.PackageManagerException
 import com.android.server.pm.PackageManagerService
 import com.android.server.pm.PackageManagerServiceUtils
+import java.io.File
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
-import java.io.File
 
 /**
  * This test parses all the system APKs on the device image to ensure that they succeed.
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
index 6cd7123..9517e49 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -24,8 +24,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
index 27fd781..01fad8f 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
@@ -21,8 +21,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
index b13d6de..b1f26c2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -23,8 +23,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
index fa69f84..349763a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -24,9 +24,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
index 856013a..71bdacb 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -22,9 +22,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Before;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
index ae5ea21..6aa0c2d 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
@@ -21,8 +21,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
index e126ffc..44098d0 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -23,8 +23,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index d0b0cf8..9d5ce8a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -28,10 +28,10 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Assume;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index c141c03..bffeb72 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -23,9 +23,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index a58604b..b114cd3 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -23,9 +23,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 09b66c1..ef9c62f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -30,21 +30,21 @@
 import android.util.SparseArray
 import android.util.SparseIntArray
 import com.android.internal.R
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils
-import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils
+import com.android.internal.pm.parsing.pkg.PackageImpl
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedApexSystemServiceImpl
+import com.android.internal.pm.pkg.component.ParsedAttributionImpl
+import com.android.internal.pm.pkg.component.ParsedComponentImpl
+import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
+import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl
+import com.android.internal.pm.pkg.component.ParsedPermissionImpl
+import com.android.internal.pm.pkg.component.ParsedProcessImpl
+import com.android.internal.pm.pkg.component.ParsedProviderImpl
+import com.android.internal.pm.pkg.component.ParsedServiceImpl
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl
 import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl
-import com.android.server.pm.pkg.component.ParsedAttributionImpl
-import com.android.server.pm.pkg.component.ParsedComponentImpl
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
-import com.android.server.pm.pkg.component.ParsedPermissionImpl
-import com.android.server.pm.pkg.component.ParsedProcessImpl
-import com.android.server.pm.pkg.component.ParsedProviderImpl
-import com.android.server.pm.pkg.component.ParsedServiceImpl
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import java.security.KeyPairGenerator
@@ -534,34 +534,34 @@
             }
         ),
         getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
-        getSetByValue({ AndroidPackageUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true),
-        getSetByValue({ AndroidPackageUtils.isOem(it) }, "isOem", PackageImpl::setOem, true),
+        getSetByValue({ AndroidPackageLegacyUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true),
+        getSetByValue({ AndroidPackageLegacyUtils.isOem(it) }, "isOem", PackageImpl::setOem, true),
         getSetByValue(
-            { AndroidPackageUtils.isPrivileged(it) },
+            { AndroidPackageLegacyUtils.isPrivileged(it) },
             "isPrivileged",
             PackageImpl::setPrivileged,
             true
         ),
         getSetByValue(
-            { AndroidPackageUtils.isProduct(it) },
+            { AndroidPackageLegacyUtils.isProduct(it) },
             "isProduct",
             PackageImpl::setProduct,
             true
         ),
         getSetByValue(
-            { AndroidPackageUtils.isVendor(it) },
+            { AndroidPackageLegacyUtils.isVendor(it) },
             "isVendor",
             PackageImpl::setVendor,
             true
         ),
         getSetByValue(
-            { AndroidPackageUtils.isSystem(it) },
+            { AndroidPackageLegacyUtils.isSystem(it) },
             "isSystem",
             PackageImpl::setSystem,
             true
         ),
         getSetByValue(
-            { AndroidPackageUtils.isSystemExt(it) },
+            { AndroidPackageLegacyUtils.isSystemExt(it) },
             "isSystemExt",
             PackageImpl::setSystemExt,
             true
@@ -593,7 +593,7 @@
                 )
             ) { "" }
         },
-        true
+        true, null
     )
         .asSplit(
             arrayOf("testSplitNameZero", "testSplitNameOne"),
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 2646854..2c8b1cd 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -18,7 +18,7 @@
 
 import android.content.pm.ActivityInfo
 import com.android.internal.pm.pkg.component.ParsedActivity
-import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
index 52d5b3b..ad53746 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedAttribution
-import com.android.server.pm.pkg.component.ParsedAttributionImpl
+import com.android.internal.pm.pkg.component.ParsedAttributionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
index af0c0de2..3ac4853 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.pm.PackageManager
 import com.android.internal.pm.pkg.component.ParsedComponent
-import com.android.server.pm.pkg.component.ParsedComponentImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
+import com.android.internal.pm.pkg.component.ParsedComponentImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import android.os.Bundle
 import android.os.Parcelable
 import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
index dc0f194..2bd4f61 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedInstrumentation
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
+import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
index 5224f23..af385e2 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedIntentInfo
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import android.os.Parcelable
 import android.os.PatternMatcher
 import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
index dfff602..061e39d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedMainComponent
-import com.android.server.pm.pkg.component.ParsedMainComponentImpl
+import com.android.internal.pm.pkg.component.ParsedMainComponentImpl
 import android.os.Parcelable
 import java.util.Arrays
 import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
index ccbf558..3a64188 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup
-import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
+import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
index 2814783..551f16d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
@@ -18,8 +18,8 @@
 
 import com.android.internal.pm.pkg.component.ParsedPermission
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup
-import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
-import com.android.server.pm.pkg.component.ParsedPermissionImpl
+import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl
+import com.android.internal.pm.pkg.component.ParsedPermissionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index 2e96046..93bdeae 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -16,9 +16,9 @@
 
 package com.android.server.pm.test.parsing.parcelling
 
-import com.android.internal.pm.pkg.component.ParsedProcess
-import com.android.server.pm.pkg.component.ParsedProcessImpl
 import android.util.ArrayMap
+import com.android.internal.pm.pkg.component.ParsedProcess
+import com.android.internal.pm.pkg.component.ParsedProcessImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index 290dbd6..1e84470 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -17,9 +17,9 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.PathPermission
-import com.android.internal.pm.pkg.component.ParsedProvider
-import com.android.server.pm.pkg.component.ParsedProviderImpl
 import android.os.PatternMatcher
+import com.android.internal.pm.pkg.component.ParsedProvider
+import com.android.internal.pm.pkg.component.ParsedProviderImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
index 3ae7e92..79d5a4f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedService
-import com.android.server.pm.pkg.component.ParsedServiceImpl
+import com.android.internal.pm.pkg.component.ParsedServiceImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
index 67dfc6d..d0ad09b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import com.android.internal.pm.pkg.component.ParsedUsesPermission
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
+import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
index 1da3a22..b21c349 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
@@ -17,37 +17,34 @@
 package com.android.server.pm.test.pkg
 
 import android.content.Intent
-import android.content.pm.overlay.OverlayPaths
 import android.content.pm.PackageManager
 import android.content.pm.PathPermission
 import android.content.pm.SharedLibraryInfo
 import android.content.pm.VersionedPackage
+import android.content.pm.overlay.OverlayPaths
 import android.os.PatternMatcher
 import android.util.ArraySet
+import com.android.internal.pm.parsing.pkg.PackageImpl
 import com.android.internal.pm.pkg.component.ParsedActivity
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedComponentImpl
 import com.android.internal.pm.pkg.component.ParsedInstrumentation
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.internal.pm.pkg.component.ParsedPermission
 import com.android.internal.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedPermissionImpl
 import com.android.internal.pm.pkg.component.ParsedProcess
+import com.android.internal.pm.pkg.component.ParsedProcessImpl
 import com.android.internal.pm.pkg.component.ParsedProvider
+import com.android.internal.pm.pkg.component.ParsedProviderImpl
 import com.android.internal.pm.pkg.component.ParsedService
 import com.android.server.pm.PackageSetting
 import com.android.server.pm.PackageSettingBuilder
-import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageState
 import com.android.server.pm.pkg.PackageUserState
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedComponentImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.component.ParsedPermissionImpl
-import com.android.server.pm.pkg.component.ParsedProcessImpl
-import com.android.server.pm.pkg.component.ParsedProviderImpl
 import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest
 import com.google.common.truth.Expect
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
 import kotlin.contracts.ExperimentalContracts
 import kotlin.reflect.KClass
 import kotlin.reflect.KFunction
@@ -55,6 +52,9 @@
 import kotlin.reflect.full.isSubtypeOf
 import kotlin.reflect.full.memberFunctions
 import kotlin.reflect.full.starProjectedType
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
 
 class PackageStateTest {
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 9341e9d..5e73d19 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -22,11 +22,11 @@
 import android.os.PatternMatcher
 import android.util.ArraySet
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.SystemConfig
 import com.android.server.compat.PlatformCompat
 import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationCollector
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index a737b90..d307608 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -29,17 +29,22 @@
 import android.util.SparseArray
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.Computer
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationEnforcer
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
+import java.util.UUID
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.test.assertFailsWith
+import kotlin.test.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -51,11 +56,6 @@
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verifyNoMoreInteractions
-import java.util.UUID
-import java.util.concurrent.atomic.AtomicBoolean
-import java.util.concurrent.atomic.AtomicInteger
-import kotlin.test.assertFailsWith
-import kotlin.test.fail
 
 @RunWith(Parameterized::class)
 class DomainVerificationEnforcerTest {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index f38df22..5edf30a3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -30,24 +30,24 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationManagerStub
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.test.assertFailsWith
 import org.junit.Test
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mockito.doReturn
-import java.util.UUID
-import java.util.concurrent.atomic.AtomicBoolean
-import kotlin.test.assertFailsWith
 
 class DomainVerificationManagerApiTest {
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 874e0d2..85f0125 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -37,27 +37,27 @@
 import android.util.SparseArray
 import android.util.Xml
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.Computer
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.testutils.mock
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.spy
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.security.PublicKey
+import java.util.UUID
 import org.junit.Test
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mockito.doReturn
-import java.io.ByteArrayInputStream
-import java.io.ByteArrayOutputStream
-import java.security.PublicKey
-import java.util.UUID
 
 class DomainVerificationPackageTest {
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 3207e6c..a5c4f6c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -25,15 +25,16 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
+import java.util.UUID
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -44,7 +45,6 @@
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.verify
-import java.util.UUID
 
 @RunWith(Parameterized::class)
 class DomainVerificationSettingsMutationTest {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index a90b7d5..ae570a3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -27,14 +27,15 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.pkg.component.ParsedActivityImpl
+import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.pkg.PackageUserStateInternal
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
 import org.junit.Test
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
@@ -42,8 +43,6 @@
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mockito.doReturn
 
-import java.util.UUID
-
 class DomainVerificationUserStateOverrideTest {
 
     companion object {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index de8b308..c2b52b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -45,14 +45,15 @@
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -67,6 +68,7 @@
 import java.io.OutputStream;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 @SmallTest
 @Presubmit
@@ -106,6 +108,18 @@
             public boolean hasFeature(String feature) {
                 return true;
             }
+
+            @androidx.annotation.NonNull
+            @Override
+            public Set<String> getHiddenApiWhitelistedApps() {
+                return new ArraySet<>();
+            }
+
+            @androidx.annotation.NonNull
+            @Override
+            public Set<String> getInstallConstraintsAllowlist() {
+                return new ArraySet<>();
+            }
         });
 
         mMockSystem.system().stageNominalSystemState();
@@ -385,7 +399,7 @@
                 findFactory(results, "test.apex.rebootless").apexInfo);
         assertThat(factoryPkg.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath);
         assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1);
-        assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue();
+        assertThat(AndroidPackageLegacyUtils.isSystem(factoryPkg)).isTrue();
     }
 
     @Test
@@ -416,7 +430,7 @@
                 findFactory(results, "test.apex.rebootless").apexInfo);
         assertThat(factoryPkg.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath);
         assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1);
-        assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue();
+        assertThat(AndroidPackageLegacyUtils.isSystem(factoryPkg)).isTrue();
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 28bd987..7b29e2a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -56,8 +56,10 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder
 import com.android.internal.R
+import com.android.internal.pm.parsing.pkg.PackageImpl
 import com.android.internal.pm.parsing.pkg.ParsedPackage
 import com.android.internal.pm.pkg.parsing.ParsingPackage
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.LocalManagerRegistry
 import com.android.server.LocalServices
 import com.android.server.LockGuard
@@ -68,10 +70,8 @@
 import com.android.server.pm.dex.DexManager
 import com.android.server.pm.dex.DynamicCodeLogger
 import com.android.server.pm.parsing.PackageParser2
-import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.snapshot.PackageDataSnapshot
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
@@ -81,14 +81,6 @@
 import com.android.server.testutils.nullable
 import com.android.server.testutils.whenever
 import com.android.server.utils.WatchedArrayMap
-import libcore.util.HexEncoding
-import org.junit.Assert
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import org.mockito.AdditionalMatchers.or
-import org.mockito.Mockito
-import org.mockito.quality.Strictness
 import java.io.File
 import java.io.IOException
 import java.nio.file.Files
@@ -97,6 +89,14 @@
 import java.util.Arrays
 import java.util.Random
 import java.util.concurrent.FutureTask
+import libcore.util.HexEncoding
+import org.junit.Assert
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import org.mockito.AdditionalMatchers.or
+import org.mockito.Mockito
+import org.mockito.quality.Strictness
 
 /**
  * A utility for mocking behavior of the system and dependencies when testing PackageManagerService
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index e685c3f..944b1aa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -21,17 +21,17 @@
 import android.content.pm.SharedLibraryInfo
 import android.content.pm.VersionedPackage
 import android.os.Build
-import android.os.storage.StorageManager
 import android.os.UserHandle
+import android.os.storage.StorageManager
 import android.util.ArrayMap
 import android.util.PackageUtils
+import com.android.internal.pm.parsing.pkg.PackageImpl
 import com.android.internal.pm.parsing.pkg.ParsedPackage
 import com.android.server.SystemConfig.SharedLibraryEntry
 import com.android.server.compat.PlatformCompat
 import com.android.server.extendedtestutils.wheneverStatic
 import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
 import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.testutils.any
 import com.android.server.testutils.eq
 import com.android.server.testutils.mock
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index ae53e70..7444403 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -19,6 +19,7 @@
 import android.app.AppOpsManager
 import android.content.Intent
 import android.content.pm.SuspendDialogInfo
+import android.content.pm.UserPackage
 import android.os.Binder
 import android.os.PersistableBundle
 import com.android.server.testutils.any
@@ -41,12 +42,18 @@
                 .thenReturn(AppOpsManager.MODE_DEFAULT)
     }
 
+    companion object {
+        val doUserPackage = UserPackage.of(TEST_USER_ID, DEVICE_OWNER_PACKAGE)
+        val platformUserPackage = UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME)
+        val testUserPackage1 = UserPackage.of(TEST_USER_ID, TEST_PACKAGE_1)
+    }
+
     @Test
     fun setPackagesSuspended() {
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
 
@@ -63,14 +70,14 @@
     fun setPackagesSuspended_emptyPackageName() {
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 null /* packageNames */, true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).isNull()
 
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOfNulls(0), true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).isEmpty()
@@ -80,7 +87,8 @@
     fun setPackagesSuspended_callerIsNotAllowed() {
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID,
+                null /* launcherExtras */, null /* dialogInfo */,
+                testUserPackage1, TEST_USER_ID,
                 Binder.getCallingUid(), false /* quarantined */)
 
         assertThat(failedNames).asList().hasSize(1)
@@ -91,7 +99,7 @@
     fun setPackagesSuspended_callerSuspendItself() {
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).asList().hasSize(1)
@@ -102,7 +110,7 @@
     fun setPackagesSuspended_nonexistentPackage() {
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).asList().hasSize(1)
@@ -115,7 +123,7 @@
             INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 knownPackages, true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)!!
 
         assertThat(failedNames.size).isEqualTo(knownPackages.size)
@@ -129,14 +137,14 @@
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
         Mockito.clearInvocations(broadcastHelper)
         assertThat(failedNames).isEmpty()
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, false /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
 
@@ -184,7 +192,7 @@
         appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1)
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */,
-                null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
                 false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -202,22 +210,22 @@
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, appExtras, null /* launcherExtras */,
-                null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
                 false /* quarantined */)
         testHandler.flush()
         Mockito.clearInvocations(broadcastHelper)
         assertThat(failedNames).isEmpty()
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+            TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
         assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
         assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
 
         suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
-            targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE },
+            targetPackages, { suspender -> suspender.packageName == DEVICE_OWNER_PACKAGE },
             TEST_USER_ID)
 
         testHandler.flush()
@@ -243,7 +251,7 @@
         launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
-                null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
                 false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -258,7 +266,7 @@
     fun isPackageSuspended() {
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
+                null /* launcherExtras */, null /* dialogInfo */, doUserPackage,
                 TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -273,13 +281,13 @@
         launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
-                null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
                 false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+            TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
     }
 
     @Test
@@ -290,57 +298,57 @@
         // Suspend.
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */, launcherExtras,
-                null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid,
                 false /* quarantined */)
         assertThat(failedNames).isEmpty()
         testHandler.flush()
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
 
         // Suspend by system.
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */, launcherExtras,
-                null /* dialogInfo */, PLATFORM_PACKAGE_NAME, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, platformUserPackage, TEST_USER_ID, deviceOwnerUid,
                 false /* quarantined */)
         assertThat(failedNames).isEmpty()
         testHandler.flush()
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME)
+                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage)
 
         // QAS by package1.
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */, launcherExtras,
-                null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid,
+                null /* dialogInfo */, testUserPackage1, TEST_USER_ID, deviceOwnerUid,
                 true /* quarantined */)
         assertThat(failedNames).isEmpty()
         testHandler.flush()
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(TEST_PACKAGE_1)
+                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(testUserPackage1)
 
         // Un-QAS by package1.
         suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
-                targetPackages, { suspendingPackage -> suspendingPackage == TEST_PACKAGE_1 },
+                targetPackages, { suspendingPackage -> suspendingPackage == testUserPackage1 },
                 TEST_USER_ID)
         testHandler.flush()
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME)
+                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage)
 
         // Un-suspend by system.
         suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
-                targetPackages, { suspendingPackage -> suspendingPackage == PLATFORM_PACKAGE_NAME },
+                targetPackages, { suspender -> suspender.packageName == PLATFORM_PACKAGE_NAME },
                 TEST_USER_ID)
         testHandler.flush()
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
-                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+                TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage)
 
         // Unsuspend.
         suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
-                targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE },
+                targetPackages, { suspendingPackage -> suspendingPackage == doUserPackage },
                 TEST_USER_ID)
         testHandler.flush()
 
@@ -354,13 +362,13 @@
             .setTitle(TEST_PACKAGE_1).build()
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+                null /* launcherExtras */, dialogInfo, doUserPackage, TEST_USER_ID,
                 deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
         val result = suspendPackageHelper.getSuspendedDialogInfo(pms.snapshotComputer(),
-            TEST_PACKAGE_1, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
+            TEST_PACKAGE_1, doUserPackage, TEST_USER_ID, deviceOwnerUid)!!
 
         assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
     }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index a78f2dc..3b5cae3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -23,6 +23,8 @@
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
 
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
 import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assert.assertThrows;
@@ -41,6 +43,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.hardware.biometrics.AuthenticationStateListener;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -108,6 +111,7 @@
     @Mock
     IFaceService mFaceService;
     @Mock
+
     AppOpsManager mAppOpsManager;
     @Mock
     private VirtualDeviceManagerInternal mVdmInternal;
@@ -404,6 +408,23 @@
                 eq(TEST_OP_PACKAGE_NAME));
     }
 
+    @Test
+    public void testRegisterAuthenticationStateListener_callsFingerprintService()
+            throws Exception {
+        mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
+        setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+        mAuthService = new AuthService(mContext, mInjector);
+        mAuthService.onStart();
+
+        final AuthenticationStateListener listener = mock(AuthenticationStateListener.class);
+
+        mAuthService.mImpl.registerAuthenticationStateListener(listener);
+
+        waitForIdle();
+        verify(mFingerprintService).registerAuthenticationStateListener(
+                eq(listener));
+    }
 
     @Test
     public void testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
index 5012335..94cb860 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -23,10 +25,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
 
 import androidx.test.filters.SmallTest;
 
@@ -40,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresFlagsDisabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
 @Presubmit
 @SmallTest
 public class SensorOverlaysTest {
@@ -97,7 +101,7 @@
     private void testShow(IUdfpsOverlayController udfps, ISidefpsController sidefps)
             throws Exception {
         final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
-        final int reason = BiometricOverlayConstants.REASON_UNKNOWN;
+        final int reason = BiometricRequestConstants.REASON_UNKNOWN;
         sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient);
 
         if (udfps != null) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 79a528c..c24227f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -18,6 +18,8 @@
 
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
 
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -56,6 +58,7 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.TestableContext;
 
 import androidx.test.filters.SmallTest;
@@ -68,6 +71,7 @@
 import com.android.server.biometrics.log.OperationContextExt;
 import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -91,6 +95,8 @@
 @SmallTest
 public class FingerprintAuthenticationClientTest {
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final int SENSOR_ID = 4;
     private static final int USER_ID = 8;
     private static final long OP_ID = 7;
@@ -128,6 +134,8 @@
     @Mock
     private ISidefpsController mSideFpsController;
     @Mock
+    private AuthenticationStateListeners mAuthenticationStateListeners;
+    @Mock
     private FingerprintSensorPropertiesInternal mSensorProps;
     @Mock
     private ClientMonitorCallback mCallback;
@@ -384,6 +392,7 @@
 
     private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block)
             throws RemoteException {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
         final FingerprintAuthenticationClient client = createClient();
 
         client.start(mCallback);
@@ -398,6 +407,49 @@
     }
 
     @Test
+    public void showHideOverlay_cancel_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.cancel());
+    }
+
+    @Test
+    public void showHideOverlay_stop_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.stopHalOperation());
+    }
+
+    @Test
+    public void showHideOverlay_error_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onError(0, 0));
+        verify(mCallback).onClientFinished(any(), eq(false));
+    }
+
+    @Test
+    public void showHideOverlay_lockout_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onLockoutTimed(5000));
+    }
+
+    @Test
+    public void showHideOverlay_lockoutPerm_sidefpsControllerRemovalRefactor()
+            throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onLockoutPermanent());
+    }
+
+    private void showHideOverlay_sidefpsControllerRemovalRefactor(
+            Consumer<FingerprintAuthenticationClient> block) throws RemoteException {
+        mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
+        final FingerprintAuthenticationClient client = createClient();
+
+        client.start(mCallback);
+
+        verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any());
+        verify(mAuthenticationStateListeners).onAuthenticationStarted(anyInt());
+
+        block.accept(client);
+
+        verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+        verify(mAuthenticationStateListeners).onAuthenticationStopped();
+    }
+
+    @Test
     public void cancelsAuthWhenNotInForeground() throws Exception {
         final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
         topTask.topActivity = new ComponentName("other", "thing");
@@ -502,7 +554,8 @@
                 mBiometricLogger, mBiometricContext,
                 true /* isStrongBiometric */,
                 null /* taskStackListener */,
-                mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication,
+                mUdfpsOverlayController, mSideFpsController, mAuthenticationStateListeners,
+                allowBackgroundAuthentication,
                 mSensorProps,
                 new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock,
                 lockoutTracker) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index c7eb1db..e7d4a2e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
+import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR;
+
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -38,6 +40,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.TestableContext;
 
 import androidx.test.filters.SmallTest;
@@ -48,6 +51,7 @@
 import com.android.server.biometrics.log.CallbackWithProbe;
 import com.android.server.biometrics.log.OperationContextExt;
 import com.android.server.biometrics.log.Probe;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -68,6 +72,8 @@
 @SmallTest
 public class FingerprintEnrollClientTest {
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final byte[] HAT = new byte[69];
     private static final int USER_ID = 8;
     private static final long REQUEST_ID = 9;
@@ -98,6 +104,8 @@
     @Mock
     private ISidefpsController mSideFpsController;
     @Mock
+    private AuthenticationStateListeners mAuthenticationStateListeners;
+    @Mock
     private FingerprintSensorPropertiesInternal mSensorProps;
     @Mock
     private ClientMonitorCallback mCallback;
@@ -271,6 +279,7 @@
 
     private void showHideOverlay(Consumer<FingerprintEnrollClient> block)
             throws RemoteException {
+        mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
         final FingerprintEnrollClient client = createClient();
 
         client.start(mCallback);
@@ -284,6 +293,44 @@
         verify(mSideFpsController).hide(anyInt());
     }
 
+    @Test
+    public void showHideOverlay_cancel_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.cancel());
+    }
+
+    @Test
+    public void showHideOverlay_stop_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.stopHalOperation());
+    }
+
+    @Test
+    public void showHideOverlay_error_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onError(0, 0));
+        verify(mCallback).onClientFinished(any(), eq(false));
+    }
+
+    @Test
+    public void showHideOverlay_result_sidefpsControllerRemovalRefactor() throws RemoteException {
+        showHideOverlay_sidefpsControllerRemovalRefactor(
+                c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
+    }
+
+    private void showHideOverlay_sidefpsControllerRemovalRefactor(
+            Consumer<FingerprintEnrollClient> block) throws RemoteException {
+        mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
+        final FingerprintEnrollClient client = createClient();
+
+        client.start(mCallback);
+
+        verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any());
+        verify(mAuthenticationStateListeners).onAuthenticationStarted(anyInt());
+
+        block.accept(client);
+
+        verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
+        verify(mAuthenticationStateListeners).onAuthenticationStopped();
+    }
+
     private FingerprintEnrollClient createClient() throws RemoteException {
         return createClient(500);
     }
@@ -296,6 +343,7 @@
         mClientMonitorCallbackConverter, 0 /* userId */,
         HAT, "owner", mBiometricUtils, 8 /* sensorId */,
         mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
-        mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL);
+        mSideFpsController, mAuthenticationStateListeners, 6 /* maxTemplatesPerUser */,
+        FingerprintManager.ENROLL_ENROLL);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 8f6efff..4cfb83f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -43,6 +43,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -74,6 +75,8 @@
     @Mock
     private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
     @Mock
+    private AuthenticationStateListeners mAuthenticationStateListeners;
+    @Mock
     private BiometricStateCallback mBiometricStateCallback;
     @Mock
     private BiometricContext mBiometricContext;
@@ -110,8 +113,9 @@
         mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
 
         mFingerprintProvider = new FingerprintProvider(mContext,
-                mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
-                mGestureAvailabilityDispatcher, mBiometricContext, mDaemon);
+                mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
+                mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
+                mDaemon);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index b32b89a..0d3f192 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -40,6 +40,7 @@
 
 import com.android.internal.R;
 import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -70,6 +71,8 @@
     @Mock
     private BiometricScheduler mScheduler;
     @Mock
+    private AuthenticationStateListeners mAuthenticationStateListeners;
+    @Mock
     private BiometricStateCallback mBiometricStateCallback;
     @Mock
     private BiometricContext mBiometricContext;
@@ -102,9 +105,10 @@
                         componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN,
                         resetLockoutRequiresHardwareAuthToken);
 
-        mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback, sensorProps,
-                mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher,
-                mHalResultController, mBiometricContext);
+        mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback,
+                mAuthenticationStateListeners, sensorProps, mScheduler,
+                new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController,
+                mBiometricContext);
     }
 
     @Test
@@ -126,13 +130,14 @@
 
         TestableFingerprint21(@NonNull Context context,
                 @NonNull BiometricStateCallback biometricStateCallback,
+                @NonNull AuthenticationStateListeners authenticationStateListeners,
                 @NonNull FingerprintSensorPropertiesInternal sensorProps,
                 @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
                 @NonNull LockoutResetDispatcher lockoutResetDispatcher,
                 @NonNull HalResultController controller,
                 @NonNull BiometricContext biometricContext) {
-            super(context, biometricStateCallback, sensorProps, scheduler, handler,
-                    lockoutResetDispatcher, controller, biometricContext);
+            super(context, biometricStateCallback, authenticationStateListeners, sensorProps,
+                    scheduler, handler, lockoutResetDispatcher, controller, biometricContext);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 943a9c47..1dd64ff 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -18,7 +18,6 @@
 import static android.os.UserHandle.USER_SYSTEM;
 
 import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -222,21 +221,21 @@
         prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
 
         // Pretend some packages are suspended.
-        when(getServices().packageManagerInternal.isSuspendingAnyPackages(
-                PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+        when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages(
+                USER_SYSTEM)).thenReturn(true);
 
         final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
 
         verify(getServices().packageManagerInternal, never())
-                .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+                .unsuspendAdminSuspendedPackages(USER_SYSTEM);
 
         sendBroadcastWithUser(dpms, Intent.ACTION_USER_STARTED, USER_SYSTEM);
 
         // Verify that actual package suspension state is not modified after user start
         verify(getServices().packageManagerInternal, never())
-                .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+                .unsuspendAdminSuspendedPackages(USER_SYSTEM);
         verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
-                any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt());
+                any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt(), anyInt());
 
         final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
         poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
@@ -255,14 +254,14 @@
         prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q);
 
         // Pretend some packages are suspended.
-        when(getServices().packageManagerInternal.isSuspendingAnyPackages(
-                PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+        when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages(
+                USER_SYSTEM)).thenReturn(true);
 
         final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
 
         // Verify that apps get unsuspended.
         verify(getServices().packageManagerInternal)
-                .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+                .unsuspendAdminSuspendedPackages(USER_SYSTEM);
 
         final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
         poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index f4dac2c..2470403 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -63,7 +63,6 @@
 import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
 import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
 import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.testutils.TestUtils.assertExpectException;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -5080,7 +5079,7 @@
         verify(getServices().iwindowManager).refreshScreenCaptureDisabled();
         // Unsuspend personal apps
         verify(getServices().packageManagerInternal)
-                .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
+                .unsuspendAdminSuspendedPackages(UserHandle.USER_SYSTEM);
         verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
                 FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
@@ -7535,7 +7534,7 @@
                 .cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
         // Verify that the apps are NOT unsuspeded.
         verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
-                any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt());
+                any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt(), anyInt());
         // Verify that DPC is invoked to check policy compliance.
         verify(mContext.spiedContext).startActivityAsUser(
                 MockUtils.checkIntentAction(ACTION_CHECK_POLICY_COMPLIANCE),
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index fe37f42..b3d25f2 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -47,6 +47,10 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public final class OverrideRequestControllerTest {
+
+    private static final DeviceState TEST_DEVICE_STATE_ZERO = new DeviceState(0, "TEST_STATE", 0);
+    private static final DeviceState TEST_DEVICE_STATE_ONE = new DeviceState(1, "TEST_STATE", 0);
+
     private TestStatusChangeListener mStatusListener;
     private OverrideRequestController mController;
 
@@ -59,7 +63,7 @@
     @Test
     public void addRequest() {
         OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
         assertNull(mStatusListener.getLastStatus(request));
 
         mController.addRequest(request);
@@ -69,14 +73,14 @@
     @Test
     public void addRequest_cancelExistingRequestThroughNewRequest() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
         assertNull(mStatusListener.getLastStatus(firstRequest));
 
         mController.addRequest(firstRequest);
         assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
 
         OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
         assertNull(mStatusListener.getLastStatus(secondRequest));
 
         mController.addRequest(secondRequest);
@@ -87,7 +91,7 @@
     @Test
     public void addRequest_cancelActiveRequest() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
         mController.addRequest(firstRequest);
 
@@ -101,7 +105,7 @@
     @Test
     public void addBaseStateRequest() {
         OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
         assertNull(mStatusListener.getLastStatus(request));
 
         mController.addBaseStateRequest(request);
@@ -111,14 +115,14 @@
     @Test
     public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
         assertNull(mStatusListener.getLastStatus(firstRequest));
 
         mController.addBaseStateRequest(firstRequest);
         assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
 
         OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+                TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
         assertNull(mStatusListener.getLastStatus(secondRequest));
 
         mController.addBaseStateRequest(secondRequest);
@@ -129,7 +133,7 @@
     @Test
     public void addBaseStateRequest_cancelActiveBaseStateRequest() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
 
         mController.addBaseStateRequest(firstRequest);
 
@@ -143,13 +147,13 @@
     @Test
     public void handleBaseStateChanged() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */,
+                TEST_DEVICE_STATE_ZERO,
                 DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */,
                 OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
         OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
                 0 /* uid */,
-                0 /* requestedState */,
+                TEST_DEVICE_STATE_ZERO,
                 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
 
         mController.addRequest(firstRequest);
@@ -169,11 +173,11 @@
     @Test
     public void handleProcessDied() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
         OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
                 0 /* uid */,
-                1 /* requestedState */,
+                TEST_DEVICE_STATE_ONE,
                 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
 
         mController.addRequest(firstRequest);
@@ -192,11 +196,11 @@
         mController.setStickyRequestsAllowed(true);
 
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
         OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
                 0 /* uid */,
-                1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+                TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
 
         mController.addRequest(firstRequest);
         assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
@@ -215,11 +219,11 @@
     @Test
     public void handleNewSupportedStates() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
         OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
                 0 /* uid */,
-                1 /* requestedState */,
+                TEST_DEVICE_STATE_ONE,
                 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
 
         mController.addRequest(firstRequest);
@@ -242,7 +246,7 @@
     @Test
     public void cancelOverrideRequestsTest() {
         OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */,
-                1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+                TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
         mController.addRequest(firstRequest);
         assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index fb78574..0d25faf 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -60,6 +60,8 @@
 
 import com.android.internal.inputmethod.StartInputFlags;
 
+import com.google.common.truth.Truth;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -1336,6 +1338,54 @@
         }
     }
 
+    private static void verifySplitEnabledImeStr(@NonNull String enabledImeStr,
+            @NonNull String... expected) {
+        final ArrayList<String> actual = new ArrayList<>();
+        InputMethodUtils.splitEnabledImeStr(enabledImeStr, actual::add);
+        if (expected.length == 0) {
+            Truth.assertThat(actual).isEmpty();
+        } else {
+            Truth.assertThat(actual).containsExactlyElementsIn(expected);
+        }
+    }
+
+    @Test
+    public void testSplitEnabledImeStr() {
+        verifySplitEnabledImeStr("");
+        verifySplitEnabledImeStr("com.android/.ime1", "com.android/.ime1");
+        verifySplitEnabledImeStr("com.android/.ime1;1;2;3", "com.android/.ime1");
+        verifySplitEnabledImeStr("com.android/.ime1;1;2;3:com.android/.ime2",
+                "com.android/.ime1", "com.android/.ime2");
+        verifySplitEnabledImeStr("com.android/.ime1:com.android/.ime2",
+                "com.android/.ime1", "com.android/.ime2");
+        verifySplitEnabledImeStr("com.android/.ime1:com.android/.ime2:com.android/.ime3",
+                "com.android/.ime1", "com.android/.ime2", "com.android/.ime3");
+        verifySplitEnabledImeStr("com.android/.ime1;1:com.android/.ime2;1:com.android/.ime3;1",
+                "com.android/.ime1", "com.android/.ime2", "com.android/.ime3");
+    }
+
+    @Test
+    public void testConcatEnabledImeIds() {
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds("")).isEmpty();
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds("", "com.android/.ime1"))
+                .isEqualTo("com.android/.ime1");
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds(
+                        "com.android/.ime1", "com.android/.ime1"))
+                .isEqualTo("com.android/.ime1");
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds(
+                        "com.android/.ime1", "com.android/.ime2"))
+                .isEqualTo("com.android/.ime1:com.android/.ime2");
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds(
+                        "com.android/.ime1", "com.android/.ime2", "com.android/.ime3"))
+                .isEqualTo("com.android/.ime1:com.android/.ime2:com.android/.ime3");
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds(
+                        "com.android/.ime1:com.android/.ime2", "com.android/.ime1"))
+                .isEqualTo("com.android/.ime1:com.android/.ime2");
+        Truth.assertThat(InputMethodUtils.concatEnabledImeIds(
+                        "com.android/.ime1:com.android/.ime2", "com.android/.ime3"))
+                .isEqualTo("com.android/.ime1:com.android/.ime2:com.android/.ime3");
+    }
+
     @Test
     public void updateEnabledImeStringTest() {
         // No change cases
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index dd687fd4..86a1358 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -27,9 +27,9 @@
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.PackageState;
 
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 8464969..ee93bc1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -53,10 +53,10 @@
 import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 0f87202..587f5fa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -29,9 +29,9 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import dalvik.system.DelegateLastClassLoader;
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index ee23a00..9b4ca2a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -21,17 +21,17 @@
 import android.os.SystemProperties.PROP_VALUE_MAX
 import android.platform.test.annotations.Postsubmit
 import com.android.internal.R
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.PackageManagerService
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.File
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertThrows
 import org.junit.Assert.fail
 import org.junit.Test
 import org.xmlpull.v1.XmlPullParser
 import org.xmlpull.v1.XmlPullParserFactory
-import java.io.ByteArrayInputStream
-import java.io.File
 
 @Postsubmit
 class AndroidPackageParsingValidationTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
index 2332817..c44f583 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
@@ -17,6 +17,7 @@
 package com.android.server.pm.parsing
 
 import android.content.pm.ApplicationInfo
+import android.util.ArraySet
 import java.io.File
 
 class TestPackageParser2(var cacheDir: File? = null) : PackageParser2(
@@ -33,4 +34,7 @@
         // behavior.
         return false
     }
+
+    override fun getHiddenApiWhitelistedApps() = ArraySet<String>()
+    override fun getInstallConstraintsAllowlist() = ArraySet<String>()
 })
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 5ba4851..759b204 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -32,8 +32,9 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
+import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.PackageList;
-import com.android.server.pm.parsing.pkg.PackageImpl;
+
 import com.google.common.collect.Range;
 
 import org.junit.Before;
@@ -415,7 +416,7 @@
 
     private void addPkgWithMinExtVersions(String pkg, int[][] minExtVersions) {
         mPackages.add(pkg);
-        PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false);
+        PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false, null);
         pkgImpl.setMinExtensionVersions(sparseArrayFrom(minExtVersions));
 
         when(mMockPmi.getPackage(pkg)).thenReturn(pkgImpl);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 14b551a..776189e 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -219,6 +219,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.ConversationChannelWrapper;
+import android.service.notification.DeviceEffectsApplier;
 import android.service.notification.NotificationListenerFilter;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
@@ -635,6 +636,10 @@
     }
 
     private void initNMS() throws Exception {
+        initNMS(SystemService.PHASE_BOOT_COMPLETED);
+    }
+
+    private void initNMS(int upToBootPhase) throws Exception {
         mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
                 mNotificationInstanceIdSequence);
 
@@ -662,13 +667,21 @@
                 mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
                 mTelecomManager, mLogger, mTestFlagResolver, mPermissionManager,
                 mPowerManager, mPostNotificationTrackerFactory);
+
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
-        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
+
+        if (upToBootPhase >= SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
+        }
+
         Mockito.reset(mHistoryManager);
         verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
-        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
-        verify(mHistoryManager).onBootPhaseAppsCanStart();
+
+        if (upToBootPhase >= SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
+            verify(mHistoryManager).onBootPhaseAppsCanStart();
+        }
 
         // TODO b/291907312: remove feature flag
         if (Flags.refactorAttentionHelper()) {
@@ -914,7 +927,9 @@
         mTestNotificationChannel.setAllowBubbles(channelEnabled);
     }
 
-    private void setUpPrefsForHistory(int uid, boolean globalEnabled) {
+    private void setUpPrefsForHistory(int uid, boolean globalEnabled) throws Exception {
+        initNMS(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+
         // Sets NOTIFICATION_HISTORY_ENABLED setting for calling process uid
         Settings.Secure.putIntForUser(mContext.getContentResolver(),
                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, uid);
@@ -10090,7 +10105,7 @@
     }
 
     @Test
-    public void testHandleOnPackageRemoved_ClearsHistory() throws RemoteException {
+    public void testHandleOnPackageRemoved_ClearsHistory() throws Exception {
         // Enables Notification History setting
         setUpPrefsForHistory(mUid, true /* =enabled */);
 
@@ -13209,6 +13224,34 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void setDeviceEffectsApplier_succeeds() throws Exception {
+        initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class));
+        // No exception!
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void setDeviceEffectsApplier_tooLate_throws() throws Exception {
+        initNMS(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
+
+        assertThrows(IllegalStateException.class, () ->
+                mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class)));
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void setDeviceEffectsApplier_calledTwice_throws() throws Exception {
+        initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class));
+        assertThrows(IllegalStateException.class, () ->
+                mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class)));
+    }
+
+    @Test
     @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
     public void setNotificationPolicy_mappedToImplicitRule() throws RemoteException {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
index 656957c..159c760 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
@@ -120,12 +120,12 @@
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_currentAndPreinstalledAssistant_setsOp() {
+    public void setShouldReceiveSandboxedTrainingData_currentAndPreinstalledAssistant_setsOp() {
         // Set application info so current app is the current and preinstalled assistant.
         mApplicationInfo.uid = Process.myUid();
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
-        mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+        mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                 /* allowed= */ true);
 
         verify(mAppOpsManager).setUidMode(mOpIdCaptor.capture(), mUidCaptor.capture(),
@@ -137,7 +137,7 @@
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_missingPermission_doesNotSetOp() {
+    public void setShouldReceiveSandboxedTrainingData_missingPermission_doesNotSetOp() {
         // Set application info so current app is the current and preinstalled assistant.
         mApplicationInfo.uid = Process.myUid();
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
@@ -146,33 +146,33 @@
         mPermissionEnforcer.revoke(Manifest.permission.MANAGE_HOTWORD_DETECTION);
 
         assertThrows(SecurityException.class,
-                () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+                () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                         /* allowed= */ true));
 
         verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_notPreinstalledAssistant_doesNotSetOp() {
+    public void setShouldReceiveSandboxedTrainingData_notPreinstalledAssistant_doesNotSetOp() {
         // Set application info so current app is not preinstalled assistant.
         mApplicationInfo.uid = Process.myUid();
         mApplicationInfo.flags = ApplicationInfo.FLAG_INSTALLED; // Does not contain FLAG_SYSTEM.
 
         assertThrows(SecurityException.class,
-                () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+                () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                                 /* allowed= */ true));
 
         verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_notCurrentAssistant_doesNotSetOp() {
+    public void setShouldReceiveSandboxedTrainingData_notCurrentAssistant_doesNotSetOp() {
         // Set application info so current app is not current assistant.
         mApplicationInfo.uid = Process.SHELL_UID; // Set current assistant uid to shell UID.
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
         assertThrows(SecurityException.class,
-                () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+                () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                                 /* allowed= */ true));
 
         verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 526201f..670f9f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -49,6 +49,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserPackage;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -195,7 +196,7 @@
         mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
 
         when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(PLATFORM_PACKAGE_NAME);
+                .thenReturn(UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME));
 
         // THEN calling intercept returns true
         assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
@@ -227,9 +228,10 @@
                 .setMessage("Test Message")
                 .setIcon(0x11110001)
                 .build();
+        UserPackage suspender = UserPackage.of(TEST_USER_ID, suspendingPackage);
         when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(suspendingPackage);
-        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
+                .thenReturn(suspender);
+        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspender,
                 TEST_USER_ID)).thenReturn(dialogInfo);
         return dialogInfo;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 98055fa..8a9c05d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -117,6 +117,7 @@
 import com.android.compatibility.common.util.DeviceConfigStateHelper;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.am.PendingIntentRecord;
+import com.android.server.pm.PackageArchiver;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
@@ -421,6 +422,7 @@
         doNothing().when(mMockPackageManager).grantImplicitAccess(
                 anyInt(), any(), anyInt(), anyInt(), anyBoolean());
         doNothing().when(mMockPackageManager).notifyPackageUse(anyString(), anyInt());
+        doReturn(mock(PackageArchiver.class)).when(mMockPackageManager).getPackageArchiver();
 
         final Intent intent = new Intent();
         intent.addFlags(launchFlags);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 50fe042..1fb7cd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -553,7 +553,7 @@
             assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
                     new InputChannel(), true /* isDragDrop */));
             mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
-                    flag, surface, 0, 0, 0, 0, 0, data);
+                    flag, surface, 0, 0, 0, 0, 0, 0, 0, data);
             assertNotNull(mToken);
 
             r.run();
@@ -575,7 +575,7 @@
 
     private void startA11yDrag(int flags, ClipData data, Runnable r) {
         mToken = mTarget.performDrag(0, 0, mWindow.mClient,
-                flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, data);
+                flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, 0, 0, data);
         assertNotNull(mToken);
         r.run();
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 6e4f13a..c902d459 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1569,13 +1569,13 @@
 
         @Override
         @EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
-        public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) {
-            super.setIsReceiveSandboxedTrainingDataAllowed_enforcePermission();
+        public void setShouldReceiveSandboxedTrainingData(boolean allowed) {
+            super.setShouldReceiveSandboxedTrainingData_enforcePermission();
 
             synchronized (this) {
                 if (mImpl == null) {
                     throw new IllegalStateException(
-                            "setIsReceiveSandboxedTrainingDataAllowed without running voice "
+                            "setShouldReceiveSandboxedTrainingData without running voice "
                                     + "interaction service");
                 }
 
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index eac4d16..cc768bc 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -16,12 +16,16 @@
 
 package android.telephony;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.TelephonyManager.CarrierRestrictionStatus;
+
+import com.android.internal.telephony.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -104,7 +108,7 @@
     private int mCarrierRestrictionDefault;
     @MultiSimPolicy
     private int mMultiSimPolicy;
-    @TelephonyManager.CarrierRestrictionStatus
+    @CarrierRestrictionStatus
     private int mCarrierRestrictionStatus;
 
     private CarrierRestrictionRules() {
@@ -293,8 +297,22 @@
         return true;
     }
 
-    /** @hide */
-    public int getCarrierRestrictionStatus() {
+    /**
+     * Get the carrier restriction status of the device.
+     * The return value of the API is as follows.
+     * <ul>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER}
+     *      if the caller and the device locked by the network are same</li>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED} if the
+     *      caller and the device locked by the network are different</li>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the
+     *      device is not locked</li>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device
+     *      locking state is unavailable or radio does not supports the feature</li>
+     * </ul>
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_RESTRICTION_STATUS)
+    public @CarrierRestrictionStatus int getCarrierRestrictionStatus() {
         return mCarrierRestrictionStatus;
     }
 
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 3e87872..8679bd4 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1866,7 +1866,7 @@
         private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
         private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
         private boolean mAlwaysOn;
-        private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR;
+        private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE;
         private boolean mEsimBootstrapProvisioning;
 
         /**
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index 12a57d5..c8cac8f 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -25,6 +25,7 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
@@ -142,7 +143,7 @@
     }
 
     /** During the transition Secondary Activity shrinks to the bottom right corner. */
-    @Presubmit
+    @FlakyTest(bugId = 315605409)
     @Test
     fun secondaryLayerShrinks() {
         flicker.assertLayers {